Blob Blame History Raw
diff --git a/cli.py b/cli.py
index 640f190..2267b86 100644
--- a/cli.py
+++ b/cli.py
@@ -45,6 +45,11 @@ import yumcommands
 
 from yum.i18n import to_unicode, to_utf8
 
+#  This is for yum-utils/yumdownloader in RHEL-5, where it isn't importing this
+# directly but did do "from cli import *", and we did have this in 3.2.22. I
+# just _love_ how python re-exports these by default.
+from yum.packages import parsePackages
+
 def sigquit(signum, frame):
     """ SIGQUIT handler for the yum cli. """
     print >> sys.stderr, "Quit signal sent - exiting immediately"
@@ -73,6 +78,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         self.logger = logging.getLogger("yum.cli")
         self.verbose_logger = logging.getLogger("yum.verbose.cli")
         self.yum_cli_commands = {}
+        self.use_txmbr_in_callback = True
         self.registerCommand(yumcommands.InstallCommand())
         self.registerCommand(yumcommands.UpdateCommand())
         self.registerCommand(yumcommands.InfoCommand())
@@ -504,30 +510,35 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         if self.gpgsigcheck(downloadpkgs) != 0:
             return 1
         
-        if self.conf.rpm_check_debug:
-            rcd_st = time.time()
-            self.verbose_logger.log(yum.logginglevels.INFO_2, 
-                 _('Running rpm_check_debug'))
-            msgs = self._run_rpm_check_debug()
-            if msgs:
-                rpmlib_only = True
-                for msg in msgs:
-                    if msg.startswith('rpmlib('):
-                        continue
-                    rpmlib_only = False
-                if rpmlib_only:
-                    print _("ERROR You need to update rpm to handle:")
-                else:
-                    print _('ERROR with rpm_check_debug vs depsolve:')
+        self.initActionTs()
+        # save our dsCallback out
+        dscb = self.dsCallback
+        self.dsCallback = None # dumb, dumb dumb dumb!
+        self.populateTs(keepold=0) # sigh
 
-                for msg in msgs:
-                    print to_utf8(msg)
+        rcd_st = time.time()
+        self.verbose_logger.log(yum.logginglevels.INFO_2, 
+             _('Running Transaction Check'))
+        msgs = self._run_rpm_check()
+        if msgs:
+            rpmlib_only = True
+            for msg in msgs:
+                if msg.startswith('rpmlib('):
+                    continue
+                rpmlib_only = False
+            if rpmlib_only:
+                print _("ERROR You need to update rpm to handle:")
+            else:
+                print _('ERROR with transaction check vs depsolve:')
 
-                if rpmlib_only:
-                    return 1, [_('RPM needs to be updated')]
-                return 1, [_('Please report this error in %s') % self.conf.bugtracker_url]
+            for msg in msgs:
+                print to_utf8(msg)
 
-            self.verbose_logger.debug('rpm_check_debug time: %0.3f' % (time.time() - rcd_st))
+            if rpmlib_only:
+                return 1, [_('RPM needs to be updated')]
+            return 1, [_('Please report this error in %s') % self.conf.bugtracker_url]
+
+        self.verbose_logger.debug('Transaction Check time: %0.3f' % (time.time() - rcd_st))
 
         tt_st = time.time()            
         self.verbose_logger.log(yum.logginglevels.INFO_2,
@@ -535,14 +546,10 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         if not self.conf.diskspacecheck:
             self.tsInfo.probFilterFlags.append(rpm.RPMPROB_FILTER_DISKSPACE)
             
+        self.ts.order() # order the transaction
+        self.ts.clean() # release memory not needed beyond this point
         
         testcb = RPMTransaction(self, test=True)
-        
-        self.initActionTs()
-        # save our dsCallback out
-        dscb = self.dsCallback
-        self.dsCallback = None # dumb, dumb dumb dumb!
-        self.populateTs(keepold=0) # sigh
         tserrors = self.ts.test(testcb)
         del testcb
 
@@ -555,7 +562,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                  self.errorSummary(errstring)
         self.verbose_logger.log(yum.logginglevels.INFO_2,
              _('Transaction Test Succeeded'))
-        del self.ts
         
         self.verbose_logger.debug('Transaction Test time: %0.3f' % (time.time() - tt_st))
         
@@ -563,10 +569,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         signal.signal(signal.SIGQUIT, signal.SIG_DFL)
         
         ts_st = time.time()
-        self.initActionTs() # make a new, blank ts to populate
-        self.populateTs(keepold=0) # populate the ts
-        self.ts.check() #required for ordering
-        self.ts.order() # order
 
         # put back our depcheck callback
         self.dsCallback = dscb
@@ -629,7 +631,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                                      ", ".join(matches))
             self.verbose_logger.log(yum.logginglevels.INFO_2, to_unicode(msg))
 
-    def _checkMaybeYouMeant(self, arg, always_output=True):
+    def _checkMaybeYouMeant(self, arg, always_output=True, rpmdb_only=False):
         """ If the update/remove argument doesn't match with case, or due
             to not being installed, tell the user. """
         # always_output is a wart due to update/remove not producing the
@@ -638,7 +640,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         # skip it.
         if not arg or arg[0] == '@':
             return
-        matches = self.doPackageLists(patterns=[arg], ignore_case=False)
+        
+        pkgnarrow='all'
+        if rpmdb_only:
+            pkgnarrow='installed'
+        
+        matches = self.doPackageLists(pkgnarrow=pkgnarrow, patterns=[arg], ignore_case=False)
         if (matches.installed or (not matches.available and
                                   self.returnInstalledPackagesByDep(arg))):
             return # Found a match so ignore
@@ -651,7 +658,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             return
 
         # No package name, so do the maybeYouMeant thing here too
-        matches = self.doPackageLists(patterns=[arg], ignore_case=True)
+        matches = self.doPackageLists(pkgnarrow=pkgnarrow, patterns=[arg], ignore_case=True)
         if not matches.installed and matches.available:
             self.verbose_logger.log(yum.logginglevels.INFO_2,
                 _('Package(s) %s%s%s available, but not installed.'),
@@ -822,7 +829,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         for arg in userlist:
             rms = self.remove(pattern=arg)
             if not rms:
-                self._checkMaybeYouMeant(arg, always_output=False)
+                self._checkMaybeYouMeant(arg, always_output=False, rpmdb_only=True)
             all_rms.extend(rms)
         
         if all_rms:
@@ -1063,13 +1070,16 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                                           os.path.exists(arg))):
                 thispkg = yum.packages.YumUrlPackage(self, self.ts, arg)
                 pkgs.append(thispkg)
+            elif self.conf.showdupesfromrepos:
+                pkgs.extend(self.pkgSack.returnPackages(patterns=[arg]))
             else:                
-                ematch, match, unmatch = self.pkgSack.matchPackageNames([arg])
-                for po in ematch + match:
-                    pkgs.append(po)
+                try:
+                    pkgs.extend(self.pkgSack.returnNewestByName(patterns=[arg]))
+                except yum.Errors.PackageSackError:
+                    pass
                 
-            results = self.findDeps(pkgs)
-            self.depListOutput(results)
+        results = self.findDeps(pkgs)
+        self.depListOutput(results)
 
         return 0, []
 
diff --git a/docs/yum.8 b/docs/yum.8
index 52f6b53..360a976 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -73,7 +73,7 @@ gnome\-packagekit application\&.
 .br
 .I \fR * version [ all | installed | available | group-* | nogroups* | grouplist | groupinfo ]
 .br
-.I \fR * history [info|list|summary|redo|undo|new|addon-info] 
+.I \fR * history [info|list|packages-list|summary|redo|undo|new|addon-info] 
 .br
 .I \fR * check
 .br 
@@ -253,7 +253,10 @@ on groups, files, provides, filelists and rpm files just like the "install" comm
 .IP
 .IP "\fBdeplist\fP"
 Produces a list of all dependencies and what packages provide those
-dependencies for the given packages.
+dependencies for the given packages. As of 3.2.30 it now just shows the latest
+version of each package that matches (this can be changed by
+using --showduplicates) and it only shows the newest providers (which can be
+changed by using --verbose).
 .IP
 .IP "\fBrepolist\fP"
 Produces a list of configured repositories. The default is to list all
@@ -316,8 +319,12 @@ The undo/redo commands take either a transaction id or the keyword last and
 an offset from the last transaction (Eg. if you've done 250 transactions,
 "last" refers to transaction 250, and "last-4" refers to transaction 246).
 
+The addon-info command takes a transaction ID, and the packages-list command
+takes a package (with wildcards).
+
 In "history list" output the Altered column also gives some extra information
-if there was something not good with the transaction.
+if there was something not good with the transaction (this is also shown at the
+end of the package column in the packages-list command).
 
 .I \fB>\fR - The rpmdb was changed, outside yum, after the transaction.
 .br
@@ -402,7 +409,11 @@ Doesn't limit packages to their latest versions in the info, list and search
 commands (will also affect plugins which use the doPackageLists() API).
 .IP "\fB\-\-installroot=root\fP" 
 Specifies an alternative installroot, relative to which all packages will be
-installed.
+installed. Think of this like doing "chroot <root> yum" except using
+\-\-installroot allows yum to work before the chroot is created.
+Note: You may also want to use the option \-\-releasever=/ when creating the
+installroot as otherwise the $releasever value is taken from the rpmdb within
+the installroot (and thus. will be empty, before creation).
 .br
 Configuration Option: \fBinstallroot\fP
 .IP "\fB\-\-enablerepo=repoidglob\fP"
@@ -458,9 +469,11 @@ Configuration Option: \fBskip_broken\fP
 .br
 .IP "\fB\-\-releasever=version\fP"
 Pretend the current release version is the given string. This is very useful
-when combined with \-\-installroot. Note that with the default upstream cachedir,
-of /var/cache/yum, using this option will corrupt your cache (and you can use
-$releasever in your cachedir configuration to stop this).
+when combined with \-\-installroot. You can also use \-\-releasever=/ to take
+the releasever information from outside the installroot.
+Note that with the default upstream cachedir, of /var/cache/yum, using this
+option will corrupt your cache (and you can use $releasever in your cachedir
+configuration to stop this).
 .PP 
 .IP "\fB\-t, \-\-tolerant\fP"
 This option currently does nothing.
diff --git a/docs/yum.conf.5 b/docs/yum.conf.5
index e1c3480..a535b79 100644
--- a/docs/yum.conf.5
+++ b/docs/yum.conf.5
@@ -474,6 +474,8 @@ bugtrackers.
 \fBcolor \fR
 Display colorized output automatically, depending on the output terminal,
 always (using ANSI codes) or never.
+Default is `auto'.
+Possible values are: auto, never, always.
 Command-line option: \fB\-\-color\fP
 
 .IP
diff --git a/etc/yum.bash b/etc/yum.bash
index f4be628..1ccb83d 100644
--- a/etc/yum.bash
+++ b/etc/yum.bash
@@ -176,9 +176,13 @@ _yum()
 {
     COMPREPLY=()
     local yum=$1
-    local cur
-    type _get_cword &>/dev/null && cur=`_get_cword` || cur=$2
-    local prev=$3
+    local cur prev
+    local -a words
+    if type _get_comp_words_by_ref &>/dev/null ; then
+        _get_comp_words_by_ref cur prev words
+    else
+        cur=$2 prev=$3 words=("${COMP_WORDS[@]}")
+    fi
     # Commands offered as completions
     local cmds=( check check-update clean deplist distro-sync downgrade
         groupinfo groupinstall grouplist groupremove help history info install
@@ -186,12 +190,12 @@ _yum()
         shell update upgrade version )
 
     local i c cmd subcmd
-    for (( i=1; i < ${#COMP_WORDS[@]}-1; i++ )) ; do
-        [[ -n $cmd ]] && subcmd=${COMP_WORDS[i]} && break
+    for (( i=1; i < ${#words[@]}-1; i++ )) ; do
+        [[ -n $cmd ]] && subcmd=${words[i]} && break
         # Recognize additional commands and aliases
         for c in ${cmds[@]} check-rpmdb distribution-synchronization erase \
             groupupdate grouperase localinstall localupdate whatprovides ; do
-            [[ ${COMP_WORDS[i]} == $c ]] && cmd=$c && break
+            [[ ${words[i]} == $c ]] && cmd=$c && break
         done
     done
 
@@ -251,7 +255,7 @@ _yum()
                     COMPREPLY=( $( compgen -W 'info list summary undo redo
                         new addon-info package-list' -- "$cur" ) )
                     ;;
-                undo|redo|addon|addon-info)
+                undo|redo|repeat|addon|addon-info)
                     COMPREPLY=( $( compgen -W "last $( $yum -d 0 -C history \
                         2>/dev/null | \
                         sed -ne 's/^[[:space:]]*\([0-9]\{1,\}\).*/\1/p' )" \
diff --git a/output.py b/output.py
index b1d92e5..85b21f8 100755
--- a/output.py
+++ b/output.py
@@ -809,20 +809,26 @@ class YumOutput:
     def depListOutput(self, results):
         """take a list of findDeps results and 'pretty print' the output"""
         
-        for pkg in results:
+        verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
+        for pkg in sorted(results):
             print _("package: %s") % pkg.compactPrint()
             if len(results[pkg]) == 0:
                 print _("  No dependencies for this package")
                 continue
 
-            for req in results[pkg]:
+            for req in sorted(results[pkg]):
                 reqlist = results[pkg][req] 
                 print _("  dependency: %s") % prco_tuple_to_string(req)
                 if not reqlist:
                     print _("   Unsatisfied dependency")
                     continue
                 
-                for po in reqlist:
+                seen = {}
+                for po in reversed(sorted(reqlist)):
+                    key = (po.name, po.arch)
+                    if not verb and key in seen:
+                        continue
+                    seen[key] = po
                     print "   provider: %s" % po.compactPrint()
 
     def format_number(self, number, SI=0, space=' '):
@@ -1619,6 +1625,18 @@ to exit.
 
             self._historyInfoCmd(mobj)
 
+    def _hpkg2from_repo(self, hpkg):
+        """ Given a pkg, find the ipkg.ui_from_repo ... if none, then
+            get an apkg. ... and put a ? in there. """
+        ipkgs = self.rpmdb.searchPkgTuple(hpkg.pkgtup)
+        if not ipkgs:
+            apkgs = self.pkgSack.searchPkgTuple(hpkg.pkgtup)
+            if not apkgs:
+                return '?'
+            return '@?' + str(apkgs[0].repoid)
+
+        return ipkgs[0].ui_from_repo
+
     def _historyInfoCmd(self, old, pats=[]):
         name = self._pwd_ui_username(old.loginuid)
 
@@ -1631,7 +1649,8 @@ to exit.
                                           _pkg_states_available.values())])[-1]
         _pkg_states_installed['maxlen'] = maxlen
         _pkg_states_available['maxlen'] = maxlen
-        def _simple_pkg(pkg, prefix_len, was_installed=False, highlight=False):
+        def _simple_pkg(pkg, prefix_len, was_installed=False, highlight=False,
+                        pkg_max_len=0):
             prefix = " " * prefix_len
             if was_installed:
                 _pkg_states = _pkg_states_installed
@@ -1655,7 +1674,9 @@ to exit.
             else:
                 (hibeg, hiend) = self._highlight('normal')
             state = utf8_width_fill(state, _pkg_states['maxlen'])
-            print "%s%s%s%s %s" % (prefix, hibeg, state, hiend, hpkg)
+            print "%s%s%s%s %-*s %s" % (prefix, hibeg, state, hiend,
+                                        pkg_max_len, hpkg,
+                                        self._hpkg2from_repo(hpkg))
 
         if type(old.tid) == type([]):
             print _("Transaction ID :"), "%u..%u" % (old.tid[0], old.tid[-1])
@@ -1726,30 +1747,37 @@ to exit.
             addon_info = self.history.return_addon_data(old.tid)
 
             # for the ones we create by default - don't display them as there
-            default_addons = set(['config-main', 'config-repos'])
+            default_addons = set(['config-main', 'config-repos', 'saved_tx'])
             non_default = set(addon_info).difference(default_addons)
             if len(non_default) > 0:
                     print _("Additional non-default information stored: %d" 
                                 % len(non_default))
 
-        print _("Transaction performed with:")
+        if old.trans_with:
+            # This is _possible_, but not common
+            print _("Transaction performed with:")
+            pkg_max_len = max((len(str(hpkg)) for hpkg in old.trans_with))
         for hpkg in old.trans_with:
-            _simple_pkg(hpkg, 4, was_installed=True)
+            _simple_pkg(hpkg, 4, was_installed=True, pkg_max_len=pkg_max_len)
         print _("Packages Altered:")
         self.historyInfoCmdPkgsAltered(old, pats)
 
         if old.trans_skip:
             print _("Packages Skipped:")
+            pkg_max_len = max((len(str(hpkg)) for hpkg in old.trans_skip))
         for hpkg in old.trans_skip:
-            _simple_pkg(hpkg, 4)
+            _simple_pkg(hpkg, 4, pkg_max_len=pkg_max_len)
 
         if old.rpmdb_problems:
             print _("Rpmdb Problems:")
         for prob in old.rpmdb_problems:
             key = "%s%s: " % (" " * 4, prob.problem)
             print self.fmtKeyValFill(key, prob.text)
+            if prob.packages:
+                pkg_max_len = max((len(str(hpkg)) for hpkg in prob.packages))
             for hpkg in prob.packages:
-                _simple_pkg(hpkg, 8, was_installed=True, highlight=hpkg.main)
+                _simple_pkg(hpkg, 8, was_installed=True, highlight=hpkg.main,
+                            pkg_max_len=pkg_max_len)
 
         if old.output:
             print _("Scriptlet output:")
@@ -1783,10 +1811,13 @@ to exit.
         # version in the transaction and now.
         all_uistates = self._history_state2uistate
         maxlen = 0
+        pkg_max_len = 0
         for hpkg in old.trans_data:
             uistate = all_uistates.get(hpkg.state, hpkg.state)
             if maxlen < len(uistate):
                 maxlen = len(uistate)
+            if pkg_max_len < len(str(hpkg)):
+                pkg_max_len = len(str(hpkg))
 
         for hpkg in old.trans_data:
             prefix = " " * 4
@@ -1813,18 +1844,18 @@ to exit.
                   hpkg.state == 'Update'):
                 ln = len(hpkg.name) + 1
                 cn = (" " * ln) + cn[ln:]
-                print "%s%s%s%s %s" % (prefix, hibeg, uistate, hiend, cn)
             elif (last is not None and
                   last.state == 'Downgrade' and last.name == hpkg.name and
                   hpkg.state == 'Downgraded'):
                 ln = len(hpkg.name) + 1
                 cn = (" " * ln) + cn[ln:]
-                print "%s%s%s%s %s" % (prefix, hibeg, uistate, hiend, cn)
             else:
                 last = None
                 if hpkg.state in ('Updated', 'Downgrade'):
                     last = hpkg
-                print "%s%s%s%s %s" % (prefix, hibeg, uistate, hiend, cn)
+            print "%s%s%s%s %-*s %s" % (prefix, hibeg, uistate, hiend,
+                                        pkg_max_len, cn,
+                                        self._hpkg2from_repo(hpkg))
 
     def historySummaryCmd(self, extcmds):
         tids, printall = self._history_list_transactions(extcmds)
@@ -1936,6 +1967,9 @@ to exit.
             of a package(s) instead of via. transactions. """
         tids = self.history.search(extcmds)
         limit = None
+        if extcmds and not tids:
+            self.logger.critical(_('Bad transaction IDs, or package(s), given'))
+            return 1, ['Failed history packages-list']
         if not tids:
             limit = 20
 
diff --git a/rpmUtils/transaction.py b/rpmUtils/transaction.py
index e8f4459..121ad5b 100644
--- a/rpmUtils/transaction.py
+++ b/rpmUtils/transaction.py
@@ -22,18 +22,13 @@ ts = None
 class TransactionWrapper:
     def __init__(self, root='/'):
         self.ts = rpm.TransactionSet(root)
-        self._methods = ['dbMatch',
-                         'check',
+        self._methods = ['check',
                          'order',
                          'addErase',
                          'addInstall',
                          'run',
-                         'IDTXload',
-                         'IDTXglob',
-                         'rollback',
                          'pgpImportPubkey',
                          'pgpPrtPkts',
-                         'Debug',
                          'problems',
                          'setFlags',
                          'setVSFlags',
@@ -54,6 +49,17 @@ class TransactionWrapper:
             self.ts = None
             self.open = False
 
+    def dbMatch(self, *args, **kwds):
+        if 'patterns' in kwds:
+            patterns = kwds.pop('patterns')
+        else:
+            patterns = []
+
+        mi = self.ts.dbMatch(*args, **kwds)
+        for (tag, tp, pat) in patterns:
+            mi.pattern(tag, tp, pat)
+        return mi
+
     def __getattr__(self, attr):
         if attr in self._methods:
             return self.getMethod(attr)
@@ -91,6 +97,9 @@ class TransactionWrapper:
     def isTsFlagSet(self, flag):
         val = self.getTsFlags()
         return bool(flag & val)
+
+    def setScriptFd(self, fd):
+        self.ts.scriptFd = fd.fileno()
         
 #    def addProblemFilter(self, filt):
 #        curfilter = self.ts.setProbFilter(0)
@@ -100,12 +109,14 @@ class TransactionWrapper:
         """tests the ts we've setup, takes a callback function and a conf dict 
            for flags and what not"""
     
+        origflags = self.getTsFlags()
         self.addTsFlag(rpm.RPMTRANS_FLAG_TEST)
         # FIXME GARBAGE - remove once this is reimplemented elsehwere
         # KEEPING FOR API COMPLIANCE ONLY
         if conf.get('diskspacecheck') == 0:
             self.ts.setProbFilter(rpm.RPMPROB_FILTER_DISKSPACE)
         tserrors = self.ts.run(cb.callback, '')
+        self.ts.setFlags(origflags)
     
         reserrors = []
         if tserrors:
diff --git a/test/skipbroken-tests.py b/test/skipbroken-tests.py
index 4e6b2c8..36a4a6d 100644
--- a/test/skipbroken-tests.py
+++ b/test/skipbroken-tests.py
@@ -1,8 +1,11 @@
 import unittest
 import logging
 import sys
+import re
 from testbase import *
 
+REGEX_PKG = re.compile(r"(\d*):?(.*)-(.*)-(.*)\.(.*)$")
+
 class SkipBrokenTests(DepsolveTests):
     ''' Test cases to test skip-broken'''
     
@@ -20,6 +23,36 @@ class SkipBrokenTests(DepsolveTests):
         po = FakePackage(name, version, release, epoch, arch, repo=self.repo)
         self.rpmdb.addPackage(po)
         return po
+
+    def _pkgstr_to_nevra(self, pkg_str):
+        '''
+        Get a nevra from from a epoch:name-version-release.arch string
+        @param pkg_str: package string
+        '''
+        res = REGEX_PKG.search(pkg_str)
+        if res:
+            (e,n,v,r,a) = res.groups()
+            if e == "": 
+                e = "0"
+            return (n,e,v,r,a)   
+        else: 
+            raise AttributeError("Illegal package string : %s" % pkg_str)
+
+    def repoString(self, pkg_str):
+        ''' 
+        Add an available package from a epoch:name-version-release.arch string
+        '''
+        (n,e,v,r,a) = self._pkgstr_to_nevra(pkg_str)
+        return self.repoPackage(n,v,r,e,a)   
+                
+            
+    def instString(self, pkg_str):
+        ''' 
+        Add an installed package from a epoch:name-version-release.arch string
+        '''
+        (n,e,v,r,a) = self._pkgstr_to_nevra(pkg_str)
+        return self.instPackage(n,v,r,e,a)   
+
            
     def testMissingReqNoSkip(self):
         ''' install fails,  because of missing req.
@@ -669,8 +702,37 @@ class SkipBrokenTests(DepsolveTests):
         self.tsInfo.addUpdate(u7, oldpo=i7)
         self.assertEquals('ok', *self.resolveCode(skip=True))
         # uncomment this line and the test will fail and you can see the output
-        self.assertResult([i1])
+        # self.assertResult([i1])
         
+    def test_conflict_looping(self):
+        ''' 
+        Skip-broken is looping
+        https://bugzilla.redhat.com/show_bug.cgi?id=681806
+        '''
+        members = [] # the result after the transaction
+        # Installed package conflicts with u1
+        i0 = self.instString('kde-l10n-4.6.0-3.fc15.1.noarch')
+        i0.addConflicts('kdepim', 'GT', ('6', '4.5.9', '0'))
+        members.append(i0)
+        i1 = self.instString('6:kdepim-4.5.94.1-1.fc14.x86_64')
+        u1 = self.repoString('7:kdepim-4.4.10-1.fc15.x86_64')
+        self.tsInfo.addUpdate(u1, oldpo=i1)
+        # u1 should be removed, because of the conflict
+        members.append(i1)
+        i2 = self.instString('6:kdepim-libs-4.5.94.1-1.fc14.x86_64')
+        u2 = self.repoString('7:kdepim-libs-4.4.10-1.fc15.x86_64')
+        self.tsInfo.addUpdate(u2, oldpo=i2)
+        members.append(u2)
+        i3 = self.instString('kdepim-runtime-libs-4.5.94.1-2.fc14.x86_64')
+        u3 = self.repoString('1:kdepim-runtime-libs-4.4.10-2.fc15.x86_64')
+        self.tsInfo.addUpdate(u3, oldpo=i3)
+        members.append(u3)
+        i4 = self.instString('kdepim-runtime-4.5.94.1-2.fc14.x86_64')
+        u4 = self.repoString('1:kdepim-runtime-4.4.10-2.fc15.x86_64')
+        self.tsInfo.addUpdate(u4, oldpo=i4)
+        members.append(u4)
+        self.assertEquals('ok', *self.resolveCode(skip=True))
+        self.assertResult(members)
     
     
     def resolveCode(self,skip = False):
diff --git a/yum.spec b/yum.spec
index a1fbc72..65a2397 100644
--- a/yum.spec
+++ b/yum.spec
@@ -194,8 +194,8 @@ exit 0
 %defattr(-,root,root)
 %doc COPYING
 %{_sysconfdir}/cron.daily/0yum.cron
-%{_sysconfdir}/yum/yum-daily.yum
-%{_sysconfdir}/yum/yum-weekly.yum
+%config(noreplace) %{_sysconfdir}/yum/yum-daily.yum
+%config(noreplace) %{_sysconfdir}/yum/yum-weekly.yum
 %{_sysconfdir}/rc.d/init.d/yum-cron
 %config(noreplace) %{_sysconfdir}/sysconfig/yum-cron
 
diff --git a/yum/__init__.py b/yum/__init__.py
index f6e8a6b..36fc203 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -349,7 +349,10 @@ class YumBase(depsolve.Depsolve):
 
         # who are we:
         self.conf.uid = os.geteuid()
-        
+        # repos are ver/arch specific so add $basearch/$releasever
+        self.conf._repos_persistdir = os.path.normpath('%s/repos/%s/%s/'
+               % (self.conf.persistdir,  self.yumvar.get('basearch', '$basearch'),
+                  self.yumvar.get('releasever', '$releasever')))        
         self.doFileLogSetup(self.conf.uid, self.conf.logfile)
         self.verbose_logger.debug('Config time: %0.3f' % (time.time() - conf_st))
         self.plugins.run('init')
@@ -418,10 +421,7 @@ class YumBase(depsolve.Depsolve):
             else:
                 thisrepo.repo_config_age = repo_age
                 thisrepo.repofile = repofn
-                # repos are ver/arch specific so add $basearch/$releasever
-                self.conf._repos_persistdir = os.path.normpath('%s/repos/%s/%s/'
-                     % (self.conf.persistdir,  self.yumvar.get('basearch', '$basearch'),
-                        self.yumvar.get('releasever', '$releasever')))
+
                 thisrepo.base_persistdir = self.conf._repos_persistdir
 
 
@@ -574,6 +574,11 @@ class YumBase(depsolve.Depsolve):
 
             self.getReposFromConfig()
 
+        #  For rhnplugin, and in theory other stuff, calling
+        # .getReposFromConfig() recurses back into this function but only once.
+        # This means that we have two points on the stack leaving the above call
+        # but only one of them can do the repos setup. BZ 678043.
+        if hasattr(self, 'prerepoconf'):
             # Recursion
             prerepoconf = self.prerepoconf
             del self.prerepoconf
@@ -1024,6 +1029,11 @@ class YumBase(depsolve.Depsolve):
         for txmbr in txmbrs:
             if kern_pkgtup is not None and txmbr.pkgtup == kern_pkgtup:
                 pass
+            elif kern_pkgtup is not None and txmbr.name == kern_pkgtup[0]:
+                #  We don't care if they've explicitly set protected on the
+                # kernel package. Because we don't allow you to uninstall the
+                # running one so it has _special_ semantics anyway.
+                continue
             elif txmbr.name not in protected:
                 continue
             if txmbr.name not in bad_togo:
@@ -1435,12 +1445,17 @@ class YumBase(depsolve.Depsolve):
         # will be we store what we thought, not what happened (so it'll be an
         # invalid cache).
         self.rpmdb.transactionResultVersion(frpmdbv)
-
         # transaction has started - all bets are off on our saved ts file
-        try:
-            os.unlink(self._ts_save_file)
-        except (IOError, OSError), e:
-            pass
+        if self._ts_save_file is not None:
+            # write the saved transaction data to the addon location in history
+            # so we can pull it back later if we need to
+            savetx_msg = open(self._ts_save_file, 'r').read()
+            self.history.write_addon_data('saved_tx', savetx_msg)
+
+            try:
+                os.unlink(self._ts_save_file)
+            except (IOError, OSError), e:
+                pass
         self._ts_save_file = None
         
         errors = self.ts.run(cb.callback, '')
@@ -1485,7 +1500,12 @@ class YumBase(depsolve.Depsolve):
 
         
         # drop out the rpm cache so we don't step on bad hdr indexes
-        self.rpmdb.dropCachedDataPostTransaction(list(self.tsInfo))
+        if (self.ts.isTsFlagSet(rpm.RPMTRANS_FLAG_TEST) or
+            resultobject.return_code):
+            self.rpmdb.dropCachedData()
+        else:
+            self.rpmdb.dropCachedDataPostTransaction(list(self.tsInfo))
+
         self.plugins.run('posttrans')
         # sync up what just happened versus what is in the rpmdb
         if not self.ts.isTsFlagSet(rpm.RPMTRANS_FLAG_TEST):
@@ -1674,8 +1694,11 @@ class YumBase(depsolve.Depsolve):
     def doLock(self, lockfile = YUM_PID_FILE):
         """perform the yum locking, raise yum-based exceptions, not OSErrors"""
         
-        # if we're not root then lock the cache
         if self.conf.uid != 0:
+            #  If we are a user, assume we are using the root cache ... so don't
+            # bother locking.
+            if self.conf.cache:
+                return
             root = self.conf.cachedir
             # Don't want <cachedir>/var/run/yum.pid ... just: <cachedir>/yum.pid
             lockfile = os.path.basename(lockfile)
@@ -1690,7 +1713,7 @@ class YumBase(depsolve.Depsolve):
                 fd = open(lockfile, 'r')
             except (IOError, OSError), e:
                 msg = _("Could not open lock %s: %s") % (lockfile, e)
-                raise Errors.LockError(1, msg)
+                raise Errors.LockError(errno.EPERM, msg)
                 
             try: oldpid = int(fd.readline())
             except ValueError:
@@ -1707,7 +1730,7 @@ class YumBase(depsolve.Depsolve):
                     else:
                         # Whoa. What the heck happened?
                         msg = _('Unable to check if PID %s is active') % oldpid
-                        raise Errors.LockError(1, msg, oldpid)
+                        raise Errors.LockError(errno.EPERM, msg, oldpid)
                 else:
                     # Another copy seems to be running.
                     msg = _('Existing lock %s: another copy is running as pid %s.') % (lockfile, oldpid)
@@ -1752,7 +1775,7 @@ class YumBase(depsolve.Depsolve):
             if not msg.errno == errno.EEXIST: 
                 # Whoa. What the heck happened?
                 errmsg = _('Could not create lock at %s: %s ') % (filename, str(msg))
-                raise Errors.LockError(msg.errno, errmsg, contents)
+                raise Errors.LockError(msg.errno, errmsg, int(contents))
             return 0
         else:
             os.write(fd, contents)
@@ -3417,7 +3440,6 @@ class YumBase(depsolve.Depsolve):
                 pkgs = po.obsoletedBy(pkgs, limit=1)
                 if pkgs:
                     already_obs = pkgs[0]
-                    continue
 
                 if already_obs:
                     self.verbose_logger.warning(_('Package %s is obsoleted by %s which is already installed'), 
@@ -3934,11 +3956,17 @@ class YumBase(depsolve.Depsolve):
                 if (po.arch != installed_pkg.arch and
                     (isMultiLibArch(po.arch) or
                      isMultiLibArch(installed_pkg.arch))):
-                    installpkgs.append(po)
+                    if updateonly:
+                        self.logger.warning(_('Package %s.%s not installed, cannot update it. Run yum install to install it instead.'), po.name, po.arch)
+                    else:
+                        installpkgs.append(po)
                 else:
                     donothingpkgs.append(po)
             elif self.allowedMultipleInstalls(po):
-                installpkgs.append(po)
+                if updateonly:
+                    self.logger.warning(_('Package %s.%s not installed, cannot update it. Run yum install to install it instead.'), po.name, po.arch)
+                else:
+                    installpkgs.append(po)
             else:
                 donothingpkgs.append(po)
 
@@ -4462,17 +4490,20 @@ class YumBase(depsolve.Depsolve):
         be imported using askcb.
         
         @param po: Package object to retrieve the key of.
-        @param askcb: Callback function to use for asking for verification.
+        @param askcb: Callback function to use for asking for permission to
+                      import a key. This is verification, but also "choice".
                       Takes arguments of the po, the userid for the key, and
                       the keyid.
-        @param fullaskcb: Callback function to use for asking for verification
-                          of a key. Differs from askcb in that it gets passed
-                          a dictionary so that we can expand the values passed.
+        @param fullaskcb: Callback function to use for asking for permission to
+                          import a key. This is verification, but also "choice".
+                          Differs from askcb in that it gets passed a
+                          dictionary so that we can expand the values passed.
         """
         repo = self.repos.getRepo(po.repoid)
         keyurls = repo.gpgkey
         key_installed = False
 
+        user_cb_fail = False
         for keyurl in keyurls:
             keys = self._retrievePublicKey(keyurl, repo)
 
@@ -4509,7 +4540,8 @@ class YumBase(depsolve.Depsolve):
                         rc = askcb(po, info['userid'], info['hexkeyid'])
 
                     if not rc:
-                        raise Errors.YumBaseError, _("Not installing key")
+                        user_cb_fail = True
+                        continue
                     
                 # Import the key
                 ts = self.rpmdb.readOnlyTS()
@@ -4520,6 +4552,9 @@ class YumBase(depsolve.Depsolve):
                 self.logger.info(_('Key imported successfully'))
                 key_installed = True
 
+        if not key_installed and user_cb_fail:
+            raise Errors.YumBaseError, _("Didn't install any keys")
+
         if not key_installed:
             raise Errors.YumBaseError, \
                   _('The GPG keys listed for the "%s" repository are ' \
@@ -4543,11 +4578,13 @@ class YumBase(depsolve.Depsolve):
         @param destdir: destination of the gpg pub ring
         @param keyurl_list: list of urls for gpg keys
         @param is_cakey: bool - are we pulling in a ca key or not
-        @param callback: Callback function to use for asking for verification
-                          of a key. Takes a dictionary of key info.
+        @param callback: Callback function to use for asking for permission to
+                         import a key. This is verification, but also "choice".
+                         Takes a dictionary of key info.
         """
 
         key_installed = False
+        user_cb_fail = False
         for keyurl in keyurl_list:
             keys = self._retrievePublicKey(keyurl, repo, getSig=not is_cakey)
             for info in keys:
@@ -4557,16 +4594,25 @@ class YumBase(depsolve.Depsolve):
                         keyurl, info['hexkeyid']))
                     key_installed = True
                     continue
-
                 # Try installing/updating GPG key
                 if is_cakey:
+                    # know where the 'imported_cakeys' file is
+                    ikf = repo.base_persistdir + '/imported_cakeys'
                     keytype = 'CA'
+                    cakeys  = []
+                    try:
+                        cakeys_d = open(ikf, 'r').read()
+                        cakeys = cakeys_d.split('\n')
+                    except (IOError, OSError):
+                        pass
+                    if str(info['hexkeyid']) in cakeys:
+                        key_installed = True
                 else:
                     keytype = 'GPG'
-
-                if repo.gpgcakey and info['has_sig'] and info['valid_sig']:
-                    key_installed = True
-                else:
+                    if repo.gpgcakey and info['has_sig'] and info['valid_sig']:
+                        key_installed = True
+                        
+                if not key_installed:
                     self._getKeyImportMessage(info, keyurl, keytype)
                     rc = False
                     if self.conf.assumeyes:
@@ -4579,7 +4625,8 @@ class YumBase(depsolve.Depsolve):
 
 
                     if not rc:
-                        raise Errors.YumBaseError, _("Not installing key for repo %s") % repo
+                        user_cb_fail = True
+                        continue
                 
                 # Import the key
                 result = misc.import_key_to_pubring(info['raw_key'], info['hexkeyid'], gpgdir=destdir)
@@ -4587,6 +4634,20 @@ class YumBase(depsolve.Depsolve):
                     raise Errors.YumBaseError, _('Key import failed')
                 self.logger.info(_('Key imported successfully'))
                 key_installed = True
+                # write out the key id to imported_cakeys in the repos basedir
+                if is_cakey and key_installed:
+                    if info['hexkeyid'] not in cakeys:
+                        ikfo = open(ikf, 'a')
+                        try:
+                            ikfo.write(info['hexkeyid']+'\n')
+                            ikfo.flush()
+                            ikfo.close()
+                        except (IOError, OSError):
+                            # maybe a warning - but in general this is not-critical, just annoying to the user
+                            pass
+
+        if not key_installed and user_cb_fail:
+            raise Errors.YumBaseError, _("Didn't install any keys for repo %s") % repo
 
         if not key_installed:
             raise Errors.YumBaseError, \
@@ -4775,26 +4836,31 @@ class YumBase(depsolve.Depsolve):
     
     def _doTestTransaction(self,callback,display=None):
         ''' Do the RPM test transaction '''
+        self.initActionTs()
+        # save our dsCallback out
+        dscb = self.dsCallback
+        self.dsCallback = None # dumb, dumb dumb dumb!
+        self.populateTs( keepold=0 ) # sigh
+
         # This can be overloaded by a subclass.    
-        if self.conf.rpm_check_debug:
-            self.verbose_logger.log(logginglevels.INFO_2, 
-                 _('Running rpm_check_debug'))
-            msgs = self._run_rpm_check_debug()
-            if msgs:
-                rpmlib_only = True
-                for msg in msgs:
-                    if msg.startswith('rpmlib('):
-                        continue
-                    rpmlib_only = False
-                if rpmlib_only:
-                    retmsgs = [_("ERROR You need to update rpm to handle:")]
-                    retmsgs.extend(msgs)
-                    raise Errors.YumRPMCheckError, retmsgs
-                retmsgs = [_('ERROR with rpm_check_debug vs depsolve:')]
-                retmsgs.extend(msgs) 
-                retmsgs.append(_('Please report this error at %s') 
-                                             % self.conf.bugtracker_url)
-                raise Errors.YumRPMCheckError,retmsgs
+        self.verbose_logger.log(logginglevels.INFO_2, 
+                 _('Running Transaction Check'))
+        msgs = self._run_rpm_check()
+        if msgs:
+            rpmlib_only = True
+            for msg in msgs:
+                if msg.startswith('rpmlib('):
+                    continue
+                rpmlib_only = False
+            if rpmlib_only:
+                retmsgs = [_("ERROR You need to update rpm to handle:")]
+                retmsgs.extend(msgs)
+                raise Errors.YumRPMCheckError, retmsgs
+            retmsgs = [_('ERROR with transaction check vs depsolve:')]
+            retmsgs.extend(msgs) 
+            retmsgs.append(_('Please report this error at %s') 
+                                         % self.conf.bugtracker_url)
+            raise Errors.YumRPMCheckError,retmsgs
         
         tsConf = {}
         for feature in ['diskspacecheck']: # more to come, I'm sure
@@ -4804,14 +4870,7 @@ class YumBase(depsolve.Depsolve):
         # overwrite the default display class
         if display:
             testcb.display = display
-        # clean out the ts b/c we have to give it new paths to the rpms 
-        del self.ts
   
-        self.initActionTs()
-        # save our dsCallback out
-        dscb = self.dsCallback
-        self.dsCallback = None # dumb, dumb dumb dumb!
-        self.populateTs( keepold=0 ) # sigh
         tserrors = self.ts.test( testcb, conf=tsConf )
         del testcb
   
@@ -4839,12 +4898,8 @@ class YumBase(depsolve.Depsolve):
             cb.display = display
         self.runTransaction( cb=cb )
 
-    def _run_rpm_check_debug(self):
+    def _run_rpm_check(self):
         results = []
-        # save our dsCallback out
-        dscb = self.dsCallback
-        self.dsCallback = None # dumb, dumb dumb dumb!
-        self.populateTs(test=1)
         self.ts.check()
         for prob in self.ts.problems():
             #  Newer rpm (4.8.0+) has problem objects, older have just strings.
@@ -4852,7 +4907,6 @@ class YumBase(depsolve.Depsolve):
             # now just be compatible.
             results.append(to_str(prob))
 
-        self.dsCallback = dscb
         return results
 
     def add_enable_repo(self, repoid, baseurls=[], mirrorlist=None, **kwargs):
diff --git a/yum/config.py b/yum/config.py
index 97e5e3d..8c966f8 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -691,6 +691,7 @@ class YumConf(StartupConf):
     metadata_expire = SecondsOption(60 * 60 * 6) # Time in seconds (6h).
     # Time in seconds (1 day). NOTE: This isn't used when using metalinks
     mirrorlist_expire = SecondsOption(60 * 60 * 24)
+    # XXX rpm_check_debug is unused, left around for API compatibility for now
     rpm_check_debug = BoolOption(True)
     disable_excludes = ListOption()    
     skip_broken = BoolOption(False)
@@ -1050,10 +1051,24 @@ def writeRawRepoFile(repo,only=None):
     # Updated the ConfigParser with the changed values    
     cfgOptions = repo.cfg.options(repo.id)
     for name,value in repo.iteritems():
+        if value is None: # Proxy
+            continue
+
+        if only is not None and name not in only:
+            continue
+
         option = repo.optionobj(name)
-        if option.default != value or name in cfgOptions :
-            if only == None or name in only:
-                ini[section_id][name] = option.tostring(value)
+        ovalue = option.tostring(value)
+        #  If the value is the same, but just interpreted ... when we don't want
+        # to keep the interpreted values.
+        if (name in ini[section_id] and
+            ovalue == varReplace(ini[section_id][name], yumvar)):
+            ovalue = ini[section_id][name]
+
+        if name not in cfgOptions and option.default == value:
+            continue
+
+        ini[section_id][name] = ovalue
     fp =file(repo.repofile,"w")               
     fp.write(str(ini))
     fp.close()
diff --git a/yum/depsolve.py b/yum/depsolve.py
index de2849a..388811d 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -69,6 +69,8 @@ class Depsolve(object):
         self._ts = None
         self._tsInfo = None
         self.dsCallback = None
+        # Callback-style switch, default to legacy (hdr, file) mode
+        self.use_txmbr_in_callback = False
         self.logger = logging.getLogger("yum.Depsolve")
         self.verbose_logger = logging.getLogger("yum.verbose.Depsolve")
 
@@ -220,8 +222,13 @@ class Depsolve(object):
                         txmbr.ts_state = 'i'
                         txmbr.output_state = TS_INSTALL
 
+                # New-style callback with just txmbr instead of full headers?
+                if self.use_txmbr_in_callback:
+                    cbkey = txmbr
+                else:
+                    cbkey = (hdr, rpmfile)
                 
-                self.ts.addInstall(hdr, (hdr, rpmfile), txmbr.ts_state)
+                self.ts.addInstall(hdr, cbkey, txmbr.ts_state)
                 self.verbose_logger.log(logginglevels.DEBUG_1,
                     _('Adding Package %s in mode %s'), txmbr.po, txmbr.ts_state)
                 if self.dsCallback:
@@ -673,11 +680,12 @@ class Depsolve(object):
         if len(self.tsInfo) != length and txmbrs:
             return CheckDeps, errormsgs
 
-        msg = '%s conflicts with %s' % (name, conflicting_po.name)
+        msg = '%s conflicts with %s' % (name, str(conflicting_po))
         errormsgs.append(msg)
         self.verbose_logger.log(logginglevels.DEBUG_1, msg)
         CheckDeps = False
-        self.po_with_problems.add((po,None,errormsgs[-1]))
+        # report the conflicting po, so skip-broken can remove it
+        self.po_with_problems.add((po,conflicting_po,errormsgs[-1]))
         return CheckDeps, errormsgs
 
     def _undoDepInstalls(self):
@@ -799,9 +807,9 @@ class Depsolve(object):
                     continue
                 done.add((po, err))
                 self.verbose_logger.log(logginglevels.DEBUG_4,
-                    _("%s from %s has depsolving problems") % (po, po.repoid))
+                    "SKIPBROKEN: %s from %s has depsolving problems" % (po, po.repoid))
                 err = err.replace('\n', '\n  --> ')
-                self.verbose_logger.log(logginglevels.DEBUG_4,"  --> %s" % err)
+                self.verbose_logger.log(logginglevels.DEBUG_4,"SKIPBROKEN:  --> %s" % err)
             return (1, errors)
 
         if not len(self.tsInfo):
diff --git a/yum/misc.py b/yum/misc.py
index 15e571f..8e81c34 100644
--- a/yum/misc.py
+++ b/yum/misc.py
@@ -252,6 +252,9 @@ class Checksums:
     def __len__(self):
         return self._len
 
+    # Note that len(x) is assert limited to INT_MAX, which is 2GB on i686.
+    length = property(fget=lambda self: self._len)
+
     def update(self, data):
         self._len += len(data)
         for sumalgo in self._sumalgos:
@@ -323,7 +326,7 @@ def checksum(sumtype, file, CHUNK=2**16, datasize=None):
 
         data = Checksums([sumtype])
         while data.read(fo, CHUNK):
-            if datasize is not None and len(data) > datasize:
+            if datasize is not None and data.length > datasize:
                 break
 
         if type(file) is types.StringType:
@@ -332,7 +335,7 @@ def checksum(sumtype, file, CHUNK=2**16, datasize=None):
             
         # This screws up the length, but that shouldn't matter. We only care
         # if this checksum == what we expect.
-        if datasize is not None and datasize != len(data):
+        if datasize is not None and datasize != data.length:
             return '!%u!%s' % (datasize, data.hexdigest(sumtype))
 
         return data.hexdigest(sumtype)
diff --git a/yum/packages.py b/yum/packages.py
index 6f61fea..db3e973 100644
--- a/yum/packages.py
+++ b/yum/packages.py
@@ -1069,6 +1069,9 @@ class YumAvailablePackage(PackageObject, RpmBase):
             
         if self.sourcerpm:
             msg += """    <rpm:sourcerpm>%s</rpm:sourcerpm>\n""" % misc.to_xml(self.sourcerpm)
+        else: # b/c yum 2.4.3 and OLD y-m-p willgfreak out if it is not there.
+            msg += """    <rpm:sourcerpm/>\n"""
+        
         msg +="""    <rpm:header-range start="%s" end="%s"/>""" % (self.hdrstart,
                                                                self.hdrend)
         msg += self._dump_pco('provides')
@@ -1243,18 +1246,32 @@ class YumHeaderPackage(YumAvailablePackage):
         self.ver = self.version
         self.rel = self.release
         self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release)
-        # Summaries "can be" empty, which rpm return [], see BZ 473239, *sigh*
-        self.summary = self.hdr['summary'] or ''
-        self.summary = misc.share_data(self.summary.replace('\n', ''))
-        self.description = self.hdr['description'] or ''
-        self.description = misc.share_data(self.description)
+        self._loaded_summary = None
+        self._loaded_description = None
         self.pkgid = self.hdr[rpm.RPMTAG_SHA1HEADER]
         if not self.pkgid:
             self.pkgid = "%s.%s" %(self.hdr['name'], self.hdr['buildtime'])
         self.packagesize = self.hdr['size']
         self.__mode_cache = {}
         self.__prcoPopulated = False
-        
+
+    def _loadSummary(self):
+        # Summaries "can be" empty, which rpm return [], see BZ 473239, *sigh*
+        if self._loaded_summary is None:
+            summary = self._get_hdr()['summary'] or ''
+            summary = misc.share_data(summary.replace('\n', ''))
+            self._loaded_summary = summary
+        return self._loaded_summary
+    summary = property(lambda x: x._loadSummary())
+
+    def _loadDescription(self):
+        if self._loaded_description is None:
+            description = self._get_hdr()['description'] or ''
+            description = misc.share_data(description)
+            self._loaded_description = description
+        return self._loaded_description
+    description = property(lambda x: x._loadDescription())
+
     def __str__(self):
         if self.epoch == '0':
             val = '%s-%s-%s.%s' % (self.name, self.version, self.release,
@@ -1828,7 +1845,6 @@ class YumInstalledPackage(YumHeaderPackage):
                 my_mode = my_st.st_mode
                 if 'ghost' in ftypes: #  This is what rpm does, although it
                     my_mode &= 0777   # doesn't usually get here.
-                    mode    &= 0777
                 if check_perms and pf.verify_mode and my_mode != pf.mode:
                     prob = _PkgVerifyProb('mode', 'mode does not match', ftypes)
                     prob.database_value = pf.mode
diff --git a/yum/parser.py b/yum/parser.py
index e46d611..fccf528 100644
--- a/yum/parser.py
+++ b/yum/parser.py
@@ -144,6 +144,11 @@ class ConfigPreProcessor:
                 # the current file returned EOF, pop it off the stack.
                 self._popfile()
         
+        # if the section is prefixed by a space then it is breaks iniparser/configparser
+        # so fix it
+        broken_sec_match = re.match(r'\s+\[(?P<section>.*)\]', line)
+        if broken_sec_match:
+            line = line.lstrip()
         # at this point we have a line from the topmost file on the stack
         # or EOF if the stack is empty
         if self._vars:
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index 0982a7c..e93df20 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -42,11 +42,6 @@ class RPMInstalledPackage(YumInstalledPackage):
     def __init__(self, rpmhdr, index, rpmdb):
         self._has_hdr = True
         YumInstalledPackage.__init__(self, rpmhdr, yumdb=rpmdb.yumdb)
-        # NOTE: We keep summary/description/url because it doesn't add much
-        # and "yum search" uses them all.
-        self.url       = rpmhdr['url']
-        # Also keep sourcerpm for pirut/etc.
-        self.sourcerpm = rpmhdr['sourcerpm']
 
         self.idx   = index
         self.rpmdb = rpmdb
@@ -67,13 +62,19 @@ class RPMInstalledPackage(YumInstalledPackage):
             raise Errors.PackageSackError, 'Rpmdb changed underneath us'
 
     def __getattr__(self, varname):
-        self.hdr = val = self._get_hdr()
-        self._has_hdr = True
-        # If these existed, then we wouldn't get here ... and nothing in the DB
-        # starts and ends with __'s. So these are missing.
-        if varname.startswith('__') and varname.endswith('__'):
+        # If these existed, then we wouldn't get here...
+        # Prevent access of __foo__, _cached_foo etc from loading the header 
+        if varname.startswith('_'):
             raise AttributeError, "%s has no attribute %s" % (self, varname)
-            
+
+        if varname != 'hdr': # Don't cache the hdr, unless explicitly requested
+            #  Note that we don't even cache the .blah value, but looking up the
+            # header is _really_ fast so it's not obvious any of it is worth it.
+            # This is different to prco etc. data, which is loaded separately.
+            val = self._get_hdr()
+        else:
+            self.hdr = val = self._get_hdr()
+            self._has_hdr = True
         if varname != 'hdr':   #  This is unusual, for anything that happens
             val = val[varname] # a lot we should preload at __init__.
                                # Also note that pkg.no_value raises KeyError.
@@ -234,7 +235,7 @@ class RPMDBPackageSack(PackageSackBase):
                 self._simple_pkgtup_list = csumpkgtups.keys()
 
         if not self._simple_pkgtup_list:
-            for (hdr, mi) in self._all_packages():
+            for (hdr, mi) in self._get_packages():
                 self._simple_pkgtup_list.append(self._hdr2pkgTuple(hdr))
             
         return self._simple_pkgtup_list
@@ -378,54 +379,36 @@ class RPMDBPackageSack(PackageSackBase):
         pass
 
     def searchAll(self, name, query_type='like'):
-        ts = self.readOnlyTS()
         result = {}
 
         # check provides
         tag = self.DEP_TABLE['provides'][0]
-        mi = ts.dbMatch()
-        mi.pattern(tag, rpm.RPMMIRE_GLOB, name)
-        for hdr in mi:
-            if hdr['name'] == 'gpg-pubkey':
-                continue
-            pkg = self._makePackageObject(hdr, mi.instance())
+        mi = self._get_packages(patterns=[(tag, rpm.RPMMIRE_GLOB, name)])
+        for hdr, idx in mi:
+            pkg = self._makePackageObject(hdr, idx)
             result.setdefault(pkg.pkgid, pkg)
-        del mi
 
         fileresults = self.searchFiles(name)
         for pkg in fileresults:
             result.setdefault(pkg.pkgid, pkg)
         
-        if self.auto_close:
-            self.ts.close()
-
         return result.values()
 
     def searchFiles(self, name):
         """search the filelists in the rpms for anything matching name"""
 
-        ts = self.readOnlyTS()
         result = {}
         
         name = os.path.normpath(name)
-        mi = ts.dbMatch('basenames', name)
         # Note that globs can't be done. As of 4.8.1:
         #   mi.pattern('basenames', rpm.RPMMIRE_GLOB, name)
         # ...produces no results.
 
-        for hdr in mi:
-            if hdr['name'] == 'gpg-pubkey':
-                continue
-            pkg = self._makePackageObject(hdr, mi.instance())
+        for hdr, idx in self._get_packages('basenames', name):
+            pkg = self._makePackageObject(hdr, idx)
             result.setdefault(pkg.pkgid, pkg)
-        del mi
-
-        result = result.values()
-
-        if self.auto_close:
-            self.ts.close()
 
-        return result
+        return result.values()
         
     def searchPrco(self, name, prcotype):
 
@@ -438,21 +421,15 @@ class RPMDBPackageSack(PackageSackBase):
         if misc.re_glob(n):
             glob = True
             
-        ts = self.readOnlyTS()
         result = {}
         tag = self.DEP_TABLE[prcotype][0]
-        mi = ts.dbMatch(tag, misc.to_utf8(n))
-        for hdr in mi:
-            if hdr['name'] == 'gpg-pubkey':
-                continue
-            po = self._makePackageObject(hdr, mi.instance())
+        for hdr, idx in self._get_packages(tag, misc.to_utf8(n)):
+            po = self._makePackageObject(hdr, idx)
             if not glob:
                 if po.checkPrco(prcotype, (n, f, (e,v,r))):
                     result[po.pkgid] = po
             else:
                 result[po.pkgid] = po
-        del mi
-
 
         # If it's not a provides or filename, we are done
         if prcotype == 'provides' and name[0] == '/':
@@ -463,9 +440,6 @@ class RPMDBPackageSack(PackageSackBase):
         result = result.values()
         self._cache[prcotype][name] = result
 
-        if self.auto_close:
-            self.ts.close()
-
         return result
 
     def searchProvides(self, name):
@@ -607,7 +581,7 @@ class RPMDBPackageSack(PackageSackBase):
 
         if not self._completely_loaded:
             rpats = self._compile_patterns(patterns, ignore_case)
-            for hdr, idx in self._all_packages():
+            for hdr, idx in self._get_packages():
                 if self._match_repattern(rpats, hdr, ignore_case):
                     self._makePackageObject(hdr, idx)
             self._completely_loaded = patterns is None
@@ -636,18 +610,13 @@ class RPMDBPackageSack(PackageSackBase):
 
         if self._cached_conflicts_data is None:
             result = {}
-            ts = self.readOnlyTS()
-            mi = ts.dbMatch('conflictname')
-
-            for hdr in mi:
-                if hdr['name'] == 'gpg-pubkey': # Just in case...
-                    continue
 
+            for hdr, idx in self._get_packages('conflictname'):
                 if not hdr[rpm.RPMTAG_CONFLICTNAME]:
                     # Pre. rpm-4.9.x the above dbMatch() does nothing.
                     continue
 
-                po = self._makePackageObject(hdr, mi.instance())
+                po = self._makePackageObject(hdr, idx)
                 result[po.pkgid] = po
                 if po._has_hdr:
                     continue # Unlikely, but, meh...
@@ -659,9 +628,6 @@ class RPMDBPackageSack(PackageSackBase):
                 del po.hdr
             self._cached_conflicts_data = result.values()
 
-            if self.auto_close:
-                self.ts.close()
-
         return self._cached_conflicts_data
 
     def _write_conflicts_new(self, pkgs, rpmdbv):
@@ -1168,7 +1134,7 @@ class RPMDBPackageSack(PackageSackBase):
         if not lowered:
             searchstrings = map(lambda x: x.lower(), searchstrings)
         ret = []
-        for hdr, idx in self._all_packages():
+        for hdr, idx in self._get_packages():
             n = self._find_search_fields(fields, searchstrings, hdr)
             if n > 0:
                 ret.append((self._makePackageObject(hdr, idx), n))
@@ -1190,41 +1156,20 @@ class RPMDBPackageSack(PackageSackBase):
         return [ self._makePackageObject(h, mi) for (h, mi) in ts.returnLeafNodes(headers=True) ]
         
     # Helper functions
-    def _all_packages(self):
-        '''Generator that yield (header, index) for all packages
+    def _get_packages(self, *args, **kwds):
+        '''dbMatch() wrapper generator that yields (header, index) for matches
         '''
         ts = self.readOnlyTS()
-        mi = ts.dbMatch()
 
-        for hdr in mi:
-            if hdr['name'] != 'gpg-pubkey':
-                yield (hdr, mi.instance())
+        mi = ts.dbMatch(*args, **kwds)
+        for h in mi:
+            if h['name'] != 'gpg-pubkey':
+                yield (h, mi.instance())
         del mi
-        if self.auto_close:
-            self.ts.close()
 
-    def _header_from_index(self, idx):
-        """returns a package header having been given an index"""
-        warnings.warn('_header_from_index() will go away in a future version of Yum.\n',
-                Errors.YumFutureDeprecationWarning, stacklevel=2)
-
-        ts = self.readOnlyTS()
-        try:
-            mi = ts.dbMatch(0, idx)
-        except (TypeError, StopIteration), e:
-            #FIXME: raise some kind of error here
-            print 'No index matching %s found in rpmdb, this is bad' % idx
-            yield None # it should REALLY not be returning none - this needs to be right
-        else:
-            hdr = mi.next()
-            yield hdr
-            del hdr
-
-        del mi
         if self.auto_close:
             self.ts.close()
 
-
     def _search(self, name=None, epoch=None, ver=None, rel=None, arch=None):
         '''List of matching packages, to zero or more of NEVRA.'''
         if name is not None and name in self._pkgname_fails:
@@ -1254,18 +1199,16 @@ class RPMDBPackageSack(PackageSackBase):
 
         ts = self.readOnlyTS()
         if name is not None:
-            mi = ts.dbMatch('name', name)
+            mi = self._get_packages('name', name)
         elif arch is not None:
-            mi = ts.dbMatch('arch', arch)
+            mi = self._get_packages('arch', arch)
         else:
-            mi = ts.dbMatch()
+            mi = self._get_packages()
             self._completely_loaded = True
 
         done = False
-        for hdr in mi:
-            if hdr['name'] == 'gpg-pubkey':
-                continue
-            po = self._makePackageObject(hdr, mi.instance())
+        for hdr, idx in mi:
+            po = self._makePackageObject(hdr, idx)
             #  We create POs out of all matching names, even if we don't return
             # them.
             self._pkgnames_loaded.add(po.name)
@@ -1277,9 +1220,6 @@ class RPMDBPackageSack(PackageSackBase):
             else:
                 ret.append(po)
 
-        if self.auto_close:
-            self.ts.close()
-
         if not done and name is not None:
             self._pkgname_fails.add(name)
 
@@ -1323,7 +1263,7 @@ class RPMDBPackageSack(PackageSackBase):
     def getHdrList(self):
         warnings.warn('getHdrList() will go away in a future version of Yum.\n',
                 DeprecationWarning, stacklevel=2)
-        return [ hdr for hdr, idx in self._all_packages() ]
+        return [ hdr for hdr, idx in self._get_packages() ]
 
     def getNameArchPkgList(self):
         warnings.warn('getNameArchPkgList() will go away in a future version of Yum.\n',
diff --git a/yum/rpmtrans.py b/yum/rpmtrans.py
index 0340153..08bf99d 100644
--- a/yum/rpmtrans.py
+++ b/yum/rpmtrans.py
@@ -25,6 +25,7 @@ import types
 import sys
 from yum.constants import *
 from yum import _
+from yum.transactioninfo import TransactionMember
 import misc
 import tempfile
 
@@ -174,11 +175,11 @@ class RPMTransaction:
         self.base = base # base yum object b/c we need so much
         self.test = test # are we a test?
         self.trans_running = False
-        self.filehandles = {}
+        self.fd = None
         self.total_actions = 0
         self.total_installed = 0
         self.complete_actions = 0
-        self.installed_pkg_names = []
+        self.installed_pkg_names = set()
         self.total_removed = 0
         self.logger = logging.getLogger('yum.filelogging.RPMInstallCallback')
         self.filelog = False
@@ -209,8 +210,7 @@ class RPMTransaction:
         io_r = tempfile.NamedTemporaryFile()
         self._readpipe = io_r
         self._writepipe = open(io_r.name, 'w+b')
-        # This is dark magic, it really needs to be "base.ts.ts".
-        self.base.ts.ts.scriptFd = self._writepipe.fileno()
+        self.base.ts.setScriptFd(self._writepipe)
         rpmverbosity = {'critical' : 'crit',
                         'emergency' : 'emerg',
                         'error' : 'err',
@@ -255,12 +255,23 @@ class RPMTransaction:
 
         return (hdr['name'], hdr['arch'], epoch, hdr['version'], hdr['release'])
 
-    def _makeHandle(self, hdr):
-        handle = '%s:%s.%s-%s-%s' % (hdr['epoch'], hdr['name'], hdr['version'],
-          hdr['release'], hdr['arch'])
+    # Find out txmbr based on the callback key. On erasures we dont know
+    # the exact txmbr but we always have a name, so return (name, txmbr)
+    # tuples so callers have less twists to deal with.
+    def _getTxmbr(self, cbkey):
+        if isinstance(cbkey, TransactionMember):
+            return (cbkey.name, cbkey)
+        elif isinstance(cbkey, tuple):
+            pkgtup = self._dopkgtup(cbkey[0])
+            txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
+            # if this is not one, somebody screwed up
+            assert len(txmbrs) == 1
+            return (txmbrs[0].name, txmbrs[0])
+        elif isinstance(cbkey, basestring):
+            return (cbkey, None)
+        else:
+            return (None, None)
 
-        return handle
-    
     def ts_done(self, package, action):
         """writes out the portions of the transaction which have completed"""
         
@@ -409,11 +420,10 @@ class RPMTransaction:
     
     
     def _transStart(self, bytes, total, h):
-        if bytes == 6:
-            self.total_actions = total
-            if self.test: return
-            self.trans_running = True
-            self.ts_all() # write out what transaction will do
+        self.total_actions = total
+        if self.test: return
+        self.trans_running = True
+        self.ts_all() # write out what transaction will do
 
     def _transProgress(self, bytes, total, h):
         pass
@@ -423,62 +433,52 @@ class RPMTransaction:
 
     def _instOpenFile(self, bytes, total, h):
         self.lastmsg = None
-        hdr = None
-        if h is not None:
-            hdr, rpmloc = h[0], h[1]
-            handle = self._makeHandle(hdr)
+        name, txmbr = self._getTxmbr(h)
+        if txmbr is not None:
+            rpmloc = txmbr.po.localPkg()
             try:
-                fd = os.open(rpmloc, os.O_RDONLY)
-            except OSError, e:
+                self.fd = file(rpmloc)
+            except IOError, e:
                 self.display.errorlog("Error: Cannot open file %s: %s" % (rpmloc, e))
             else:
-                self.filehandles[handle]=fd
                 if self.trans_running:
                     self.total_installed += 1
                     self.complete_actions += 1
-                    self.installed_pkg_names.append(hdr['name'])
-                return fd
+                    self.installed_pkg_names.add(name)
+                return self.fd.fileno()
         else:
             self.display.errorlog("Error: No Header to INST_OPEN_FILE")
             
     def _instCloseFile(self, bytes, total, h):
-        hdr = None
-        if h is not None:
-            hdr, rpmloc = h[0], h[1]
-            handle = self._makeHandle(hdr)
-            os.close(self.filehandles[handle])
-            fd = 0
+        name, txmbr = self._getTxmbr(h)
+        if txmbr is not None:
+            self.fd.close()
+            self.fd = None
             if self.test: return
             if self.trans_running:
-                pkgtup = self._dopkgtup(hdr)
-                txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
-                for txmbr in txmbrs:
-                    self.display.filelog(txmbr.po, txmbr.output_state)
-                    self._scriptout(txmbr.po)
-                    # NOTE: We only do this for install, not erase atm.
-                    #       because we don't get pkgtup data for erase (this 
-                    #       includes "Updated" pkgs).
-                    pid   = self.base.history.pkg2pid(txmbr.po)
-                    state = self.base.history.txmbr2state(txmbr)
-                    self.base.history.trans_data_pid_end(pid, state)
-                    self.ts_done(txmbr.po, txmbr.output_state)
+                self.display.filelog(txmbr.po, txmbr.output_state)
+                self._scriptout(txmbr.po)
+                # NOTE: We only do this for install, not erase atm.
+                #       because we don't get pkgtup data for erase (this 
+                #       includes "Updated" pkgs).
+                pid   = self.base.history.pkg2pid(txmbr.po)
+                state = self.base.history.txmbr2state(txmbr)
+                self.base.history.trans_data_pid_end(pid, state)
+                self.ts_done(txmbr.po, txmbr.output_state)
     
     def _instProgress(self, bytes, total, h):
-        if h is not None:
-            # If h is a string, we're repackaging.
+        name, txmbr = self._getTxmbr(h)
+        if name is not None:
+            # If we only have a name, we're repackaging.
             # Why the RPMCALLBACK_REPACKAGE_PROGRESS flag isn't set, I have no idea
-            if type(h) == type(""):
-                self.display.event(h, 'repackaging',  bytes, total,
+            if txmbr is None:
+                self.display.event(name, 'repackaging',  bytes, total,
                                 self.complete_actions, self.total_actions)
-
             else:
-                hdr, rpmloc = h[0], h[1]
-                pkgtup = self._dopkgtup(hdr)
-                txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
-                for txmbr in txmbrs:
-                    action = txmbr.output_state
-                    self.display.event(txmbr.po, action, bytes, total,
-                                self.complete_actions, self.total_actions)
+                action = txmbr.output_state
+                self.display.event(txmbr.po, action, bytes, total,
+                            self.complete_actions, self.total_actions)
+
     def _unInstStart(self, bytes, total, h):
         pass
         
@@ -486,20 +486,21 @@ class RPMTransaction:
         pass
     
     def _unInstStop(self, bytes, total, h):
+        name, txmbr = self._getTxmbr(h)
         self.total_removed += 1
         self.complete_actions += 1
-        if h not in self.installed_pkg_names:
-            self.display.filelog(h, TS_ERASE)
+        if name not in self.installed_pkg_names:
+            self.display.filelog(name, TS_ERASE)
             action = TS_ERASE
         else:
             action = TS_UPDATED                    
         
-        self.display.event(h, action, 100, 100, self.complete_actions,
+        self.display.event(name, action, 100, 100, self.complete_actions,
                             self.total_actions)
-        self._scriptout(h)
+        self._scriptout(name)
         
         if self.test: return # and we're done
-        self.ts_done(h, action)
+        self.ts_done(name, action)
         
         
     def _rePackageStart(self, bytes, total, h):
@@ -512,20 +513,16 @@ class RPMTransaction:
         pass
         
     def _cpioError(self, bytes, total, h):
-        hdr, rpmloc = h[0], h[1]
-        pkgtup = self._dopkgtup(hdr)
-        txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
-        for txmbr in txmbrs:
+        name, txmbr = self._getTxmbr(h)
+        if txmbr is not None:
             msg = "Error in cpio payload of rpm package %s" % txmbr.po
             txmbr.output_state = TS_FAILED
             self.display.errorlog(msg)
             # FIXME - what else should we do here? raise a failure and abort?
     
     def _unpackError(self, bytes, total, h):
-        hdr, rpmloc = h[0], h[1]
-        pkgtup = self._dopkgtup(hdr)
-        txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
-        for txmbr in txmbrs:
+        name, txmbr = self._getTxmbr(h)
+        if txmbr is not None:
             txmbr.output_state = TS_FAILED
             msg = "Error unpacking rpm package %s" % txmbr.po
             self.display.errorlog(msg)
@@ -533,35 +530,24 @@ class RPMTransaction:
             # right behavior should be
                 
     def _scriptError(self, bytes, total, h):
-        if not isinstance(h, types.TupleType):
-            # fun with install/erase transactions, see rhbz#484729
-            h = (h, None)
-        hdr, rpmloc = h[0], h[1]
-        remove_hdr = False # if we're in a clean up/remove then hdr will not be an rpm.hdr
-        if not isinstance(hdr, rpm.hdr):
-            txmbrs = [hdr]
-            remove_hdr = True
+        # "bytes" carries the failed scriptlet tag,
+        # "total" carries fatal/non-fatal status
+        scriptlet_name = rpm.tagnames.get(bytes, "<unknown>")
+
+        name, txmbr = self._getTxmbr(h)
+        if txmbr is None:
+            package_name = name
         else:
-            pkgtup = self._dopkgtup(hdr)
-            txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
+            package_name = txmbr.po
             
-        for pkg in txmbrs:
-            # "bytes" carries the failed scriptlet tag,
-            # "total" carries fatal/non-fatal status
-            scriptlet_name = rpm.tagnames.get(bytes, "<unknown>")
-            if remove_hdr:
-                package_name = pkg
-            else:
-                package_name = pkg.po
-                
-            if total:
-                msg = ("Error in %s scriptlet in rpm package %s" % 
-                        (scriptlet_name, package_name))
-                if not remove_hdr:        
-                    pkg.output_state = TS_FAILED
-            else:
-                msg = ("Non-fatal %s scriptlet failure in rpm package %s" % 
-                       (scriptlet_name, package_name))
-            self.display.errorlog(msg)
-            # FIXME - what else should we do here? raise a failure and abort?
+        if total:
+            msg = ("Error in %s scriptlet in rpm package %s" % 
+                    (scriptlet_name, package_name))
+            if txmbr is not None:        
+                txmbr.output_state = TS_FAILED
+        else:
+            msg = ("Non-fatal %s scriptlet failure in rpm package %s" % 
+                   (scriptlet_name, package_name))
+        self.display.errorlog(msg)
+        # FIXME - what else should we do here? raise a failure and abort?
     
diff --git a/yum/update_md.py b/yum/update_md.py
index 83e56c6..39fa72e 100644
--- a/yum/update_md.py
+++ b/yum/update_md.py
@@ -32,6 +32,15 @@ import Errors
 
 import rpmUtils.miscutils
 
+
+def safe_iterparse(filename):
+    """ Works like iterparse, but hides XML errors (prints a warning). """
+    try:
+        for event, elem in iterparse(filename):
+            yield event, elem
+    except SyntaxError: # Bad XML
+        print >> sys.stderr, "File is not valid XML:", filename
+
 class UpdateNoticeException(Exception):
     """ An exception thrown for bad UpdateNotice data. """
     pass
@@ -445,7 +454,7 @@ class UpdateMetadata(object):
         else:   # obj is a file object
             infile = obj
 
-        for event, elem in iterparse(infile):
+        for event, elem in safe_iterparse(infile):
             if elem.tag == 'update':
                 try:
                     un = UpdateNotice(elem)
diff --git a/yumcommands.py b/yumcommands.py
index ecce347..41f0092 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -46,7 +46,7 @@ def checkRootUID(base):
 def checkGPGKey(base):
     if not base.gpgKeyCheck():
         for repo in base.repos.listEnabled():
-            if (repo.gpgcheck or repo.repo_gpgcheck) and repo.gpgkey == '':
+            if (repo.gpgcheck or repo.repo_gpgcheck) and not repo.gpgkey:
                 msg = _("""
 You have enabled checking of packages via GPG keys. This is a good thing. 
 However, you do not have any GPG public keys installed. You need to download
@@ -283,7 +283,7 @@ class InfoCommand(YumCommand):
         return ['info']
 
     def getUsage(self):
-        return "[PACKAGE|all|installed|updates|extras|obsoletes|recent]"
+        return "[PACKAGE|all|available|installed|updates|extras|obsoletes|recent]"
 
     def getSummary(self):
         return _("Display details about a package or group of packages")
@@ -626,13 +626,14 @@ class CheckUpdateCommand(YumCommand):
         checkEnabledRepo(base)
 
     def doCommand(self, base, basecmd, extcmds):
+        obscmds = ['obsoletes'] + extcmds
         base.extcmds.insert(0, 'updates')
         result = 0
         try:
             ypl = base.returnPkgLists(extcmds)
             if (base.conf.obsoletes or
                 base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)):
-                typl = base.returnPkgLists(['obsoletes'])
+                typl = base.returnPkgLists(obscmds)
                 ypl.obsoletes = typl.obsoletes
                 ypl.obsoletesTuples = typl.obsoletesTuples
 
@@ -972,6 +973,12 @@ class RepoListCommand(YumCommand):
                     elif repo.mirrorlist:
                         out += [base.fmtKeyValFill(_("Repo-mirrors : "),
                                                    repo.mirrorlist)]
+                    if enabled and repo.urls:
+                        url = repo.urls[0]
+                        if len(repo.urls) > 1:
+                            url += ' (%d more)' % (len(repo.urls) - 1)
+                        out += [base.fmtKeyValFill(_("Repo-baseurl : "),
+                                                   url)]
 
                     if not os.path.exists(repo.metadata_cookie):
                         last = _("Unknown")
diff --git a/yummain.py b/yummain.py
index c64b140..d0b8251 100755
--- a/yummain.py
+++ b/yummain.py
@@ -23,6 +23,7 @@ import os.path
 import sys
 import logging
 import time
+import errno
 
 from yum import Errors
 from yum import plugins
@@ -75,6 +76,15 @@ def main(args):
             return 200
         return 0
 
+    def rpmdb_warn_checks():
+        try:
+            probs = base._rpmdb_warn_checks(out=verbose_logger.info, warn=False)
+        except YumBaseError, e:
+            # This is mainly for PackageSackError from rpmdb.
+            verbose_logger.info(_(" Yum checks failed: %s"), exception2msg(e))
+            probs = []
+        if not probs:
+            verbose_logger.info(_(" You could try running: rpm -Va --nofiles --nodigest"))
 
     logger = logging.getLogger("yum.main")
     verbose_logger = logging.getLogger("yum.verbose.main")
@@ -99,12 +109,16 @@ def main(args):
             if exception2msg(e) != lockerr:
                 lockerr = exception2msg(e)
                 logger.critical(lockerr)
-            if not base.conf.exit_on_lock:
+            if (e.errno not in (errno.EPERM, errno.EACCES) and
+                not base.conf.exit_on_lock):
                 logger.critical(_("Another app is currently holding the yum lock; waiting for it to exit..."))
                 tm = 0.1
                 if show_lock_owner(e.pid, logger):
                     tm = 2
                 time.sleep(tm)
+            elif e.errno in (errno.EPERM, errno.EACCES):
+                logger.critical(_("Can't create lock file; exiting"))
+                return 1
             else:
                 logger.critical(_("Another app is currently holding the yum lock; exiting as configured by exit_on_lock"))
                 return 1
@@ -177,8 +191,7 @@ def main(args):
             logger.critical(prefix, msg.replace('\n', '\n' + prefix2nd))
         if not base.conf.skip_broken:
             verbose_logger.info(_(" You could try using --skip-broken to work around the problem"))
-        if not base._rpmdb_warn_checks(out=verbose_logger.info, warn=False):
-            verbose_logger.info(_(" You could try running: rpm -Va --nofiles --nodigest"))
+        rpmdb_warn_checks()
         if unlock(): return 200
         return 1
     elif result == 2:
@@ -205,13 +218,12 @@ def main(args):
     except IOError, e:
         return exIOError(e)
 
-    # rpm_check_debug failed.
+    # rpm ts.check() failed.
     if type(return_code) == type((0,)) and len(return_code) == 2:
         (result, resultmsgs) = return_code
         for msg in resultmsgs:
             logger.critical("%s", msg)
-        if not base._rpmdb_warn_checks(out=verbose_logger.info, warn=False):
-            verbose_logger.info(_(" You could try running: rpm -Va --nofiles --nodigest"))
+        rpmdb_warn_checks()
         return_code = result
         if base._ts_save_file:
             verbose_logger.info(_("Your transaction was saved, rerun it with: yum load-transaction %s") % base._ts_save_file)