Imported Upstream version 17.14.1
[platform/upstream/libzypp.git] / zypp / target / rpm / RpmDb.cc
index 10bd618..7203d79 100644 (file)
  *
 */
 #include "librpm.h"
-
+extern "C"
+{
+#include <rpm/rpmcli.h>
+#include <rpm/rpmlog.h>
+}
 #include <cstdlib>
 #include <cstdio>
 #include <ctime>
 
 #include <iostream>
 #include <fstream>
+#include <sstream>
 #include <list>
 #include <map>
 #include <set>
@@ -26,6 +31,8 @@
 
 #include "zypp/base/Logger.h"
 #include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/LocaleGuard.h"
 
 #include "zypp/Date.h"
 #include "zypp/Pathname.h"
 #include "zypp/target/rpm/RpmDb.h"
 #include "zypp/target/rpm/RpmCallbacks.h"
 
-#include "zypp/target/CommitLog.h"
+#include "zypp/HistoryLog.h"
 #include "zypp/target/rpm/librpmDb.h"
-#include "zypp/target/rpm/RpmPackageImpl.h"
 #include "zypp/target/rpm/RpmException.h"
-#include "zypp/CapSet.h"
-#include "zypp/CapFactory.h"
+#include "zypp/TmpPath.h"
 #include "zypp/KeyRing.h"
 #include "zypp/ZYppFactory.h"
-#include "zypp/TmpPath.h"
+#include "zypp/ZConfig.h"
 
-#ifndef _
-#define _(X) X
-#endif
-
-using namespace std;
+using std::endl;
 using namespace zypp::filesystem;
 
+#define WARNINGMAILPATH                "/var/log/YaST2/"
+#define FILEFORBACKUPFILES     "YaSTBackupModifiedFiles"
+#define MAXRPMMESSAGELINES     10000
+
+#define WORKAROUNDRPMPWDBUG
+
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "librpmDb"
+
 namespace zypp
 {
-  namespace target
+  namespace zypp_readonly_hack
+  {
+    bool IGotIt(); // in readonly-mode
+  }
+namespace target
+{
+namespace rpm
+{
+namespace
+{
+#if 1 // No more need to escape whitespace since rpm-4.4.2.3
+const char* quoteInFilename_m = "\'\"";
+#else
+const char* quoteInFilename_m = " \t\'\"";
+#endif
+inline std::string rpmQuoteFilename( const Pathname & path_r )
+{
+  std::string path( path_r.asString() );
+  for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
+        pos != std::string::npos;
+        pos = path.find_first_of( quoteInFilename_m, pos ) )
+  {
+    path.insert( pos, "\\" );
+    pos += 2; // skip '\\' and the quoted char.
+  }
+  return path;
+}
+
+
+  /** Workaround bnc#827609 - rpm needs a readable pwd so we
+   * chdir to /. Turn realtive pathnames into absolute ones
+   * by prepending cwd so rpm still finds them
+   */
+  inline Pathname workaroundRpmPwdBug( Pathname path_r )
   {
-    namespace rpm
+#if defined(WORKAROUNDRPMPWDBUG)
+    if ( path_r.relative() )
     {
+      // try to prepend cwd
+      AutoDispose<char*> cwd( ::get_current_dir_name(), ::free );
+      if ( cwd )
+       return Pathname( cwd ) / path_r;
+      WAR << "Can't get cwd!" << endl;
+    }
+#endif
+    return path_r;     // no problem with absolute pathnames
+  }
+}
 
-      struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
-      {
-        KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
-        {
-          connect();
-        }
-
-        ~KeyRingSignalReceiver()
-        {
-          disconnect();
-        }
+struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
+{
+  KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
+  {
+    connect();
+  }
 
-        virtual void trustedKeyAdded( const KeyRing &keyring, const PublicKey &key )
-        {
-          MIL << "trusted key added to zypp Keyring. Syncronizing keys with rpm keyring" << std::endl;
-          _rpmdb.importZyppKeyRingTrustedKeys();
-          _rpmdb.exportTrustedKeysInZyppKeyRing();
-        }
+  ~KeyRingSignalReceiver()
+  {
+    disconnect();
+  }
 
-        virtual void trustedKeyRemoved( const KeyRing &keyring, const PublicKey &key  )
-        {
-        }
+  virtual void trustedKeyAdded( const PublicKey &key )
+  {
+    MIL << "trusted key added to zypp Keyring. Importing..." << endl;
+    _rpmdb.importPubkey( key );
+  }
 
-        RpmDb &_rpmdb;
-      };
+  virtual void trustedKeyRemoved( const PublicKey &key  )
+  {
+    MIL << "Trusted key removed from zypp Keyring. Removing..." << endl;
+    _rpmdb.removePubkey( key );
+  }
 
-      static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
+  RpmDb &_rpmdb;
+};
 
-      unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
-      {
-        const char* argv[] =
-          {
-            "diff",
-            "-u",
-            file1.c_str(),
-            file2.c_str(),
-            NULL
-          };
-        ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
-
-        //if(!prog)
-        //return 2;
-
-        string line;
-        int count = 0;
-        for (line = prog.receiveLine(), count=0;
-             !line.empty();
-             line = prog.receiveLine(), count++ )
-        {
-          if (maxlines<0?true:count<maxlines)
-            out+=line;
-        }
+static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
 
-        return prog.close();
-      }
+unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
+{
+  const char* argv[] =
+    {
+      "diff",
+      "-u",
+      file1.c_str(),
+      file2.c_str(),
+      NULL
+    };
+  ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
+
+  //if(!prog)
+  //return 2;
+
+  std::string line;
+  int count = 0;
+  for (line = prog.receiveLine(), count=0;
+       !line.empty();
+       line = prog.receiveLine(), count++ )
+  {
+    if (maxlines<0?true:count<maxlines)
+      out+=line;
+  }
 
+  return prog.close();
+}
 
 
-      /******************************************************************
-      **
-      **
-      **       FUNCTION NAME : stringPath
-      **       FUNCTION TYPE : inline string
-      */
-      inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
-      {
-        return librpmDb::stringPath( root_r, sub_r );
-      }
 
-      /******************************************************************
-      **
-      **
-      **       FUNCTION NAME : operator<<
-      **       FUNCTION TYPE : ostream &
-      */
-      ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
-      {
-        if ( obj == RpmDb::DbSI_NO_INIT )
-        {
-          str << "NO_INIT";
-        }
-        else
-        {
+/******************************************************************
+ **
+ **
+ **    FUNCTION NAME : stringPath
+ **    FUNCTION TYPE : inline std::string
+*/
+inline std::string stringPath( const Pathname & root_r, const Pathname & sub_r )
+{
+  return librpmDb::stringPath( root_r, sub_r );
+}
+
+/******************************************************************
+ **
+ **
+ **    FUNCTION NAME : operator<<
+ **    FUNCTION TYPE : std::ostream &
+*/
+std::ostream & operator<<( std::ostream & str, const RpmDb::DbStateInfoBits & obj )
+{
+  if ( obj == RpmDb::DbSI_NO_INIT )
+  {
+    str << "NO_INIT";
+  }
+  else
+  {
 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
-          str << "V4(";
-          ENUM_OUT( DbSI_HAVE_V4,      'X' );
-          ENUM_OUT( DbSI_MADE_V4,      'c' );
-          ENUM_OUT( DbSI_MODIFIED_V4,  'm' );
-          str << ")V3(";
-          ENUM_OUT( DbSI_HAVE_V3,      'X' );
-          ENUM_OUT( DbSI_HAVE_V3TOV4,  'B' );
-          ENUM_OUT( DbSI_MADE_V3TOV4,  'c' );
-          str << ")";
+    str << "V4(";
+    ENUM_OUT( DbSI_HAVE_V4,    'X' );
+    ENUM_OUT( DbSI_MADE_V4,    'c' );
+    ENUM_OUT( DbSI_MODIFIED_V4,        'm' );
+    str << ")V3(";
+    ENUM_OUT( DbSI_HAVE_V3,    'X' );
+    ENUM_OUT( DbSI_HAVE_V3TOV4,        'B' );
+    ENUM_OUT( DbSI_MADE_V3TOV4,        'c' );
+    str << ")";
 #undef ENUM_OUT
-        }
-        return str;
-      }
-
-///////////////////////////////////////////////////////////////////
-//     CLASS NAME : RpmDbPtr
-//     CLASS NAME : RpmDbconstPtr
-///////////////////////////////////////////////////////////////////
+  }
+  return str;
+}
 
-#define WARNINGMAILPATH "/var/log/YaST2/"
-#define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
-
-///////////////////////////////////////////////////////////////////
-//
-//     CLASS NAME : RpmDb::Packages
-      /**
-       * Helper class for RpmDb::getPackages() to build the
-       * list<Package::Ptr> returned. We have to assert, that there
-       * is a unique entry for every string.
-       *
-       * In the first step we build the _list list which contains all
-       * packages (even those which are contained in multiple versions).
-       *
-       * At the end buildIndex() is called to build the _index is created
-       * and points to the last installed versions of all packages.
-       * Operations changing the rpmdb
-       * content (install/remove package) should set _valid to false. The
-       * next call to RpmDb::getPackages() will then reread the the rpmdb.
-       *
-       * Note that outside RpmDb::getPackages() _list and _index are always
-       * in sync. So you may use lookup(PkgName) to retrieve a specific
-       * Package::Ptr.
-       **/
-      class RpmDb::Packages
-      {
-      public:
-        list<Package::Ptr>        _list;
-        map<std::string,Package::Ptr> _index;
-        bool                      _valid;
-        Packages() : _valid( false )
-        {}
-        void clear()
-        {
-          _list.clear();
-          _index.clear();
-          _valid = false;
-        }
-        Package::Ptr lookup( const string & name_r ) const
-        {
-          map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
-          if ( got != _index.end() )
-            return got->second;
-          return Package::Ptr();
-        }
-        void buildIndex()
-        {
-          _index.clear();
-          for ( list<Package::Ptr>::iterator iter = _list.begin();
-                iter != _list.end(); ++iter )
-          {
-            string name = (*iter)->name();
-            Package::Ptr & nptr = _index[name]; // be shure to get a reference!
-
-            if ( nptr )
-            {
-              WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
-              if ( nptr->installtime() > (*iter)->installtime() )
-                continue;
-              else
-                nptr = *iter;
-            }
-            else
-            {
-              nptr = *iter;
-            }
-          }
-          _valid = true;
-        }
-      };
 
-///////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -251,22 +230,21 @@ namespace zypp
 //     METHOD NAME : RpmDb::RpmDb
 //     METHOD TYPE : Constructor
 //
-      RpmDb::RpmDb()
-          : _dbStateInfo( DbSI_NO_INIT )
-          , _packages( * new Packages ) // delete in destructor
+RpmDb::RpmDb()
+    : _dbStateInfo( DbSI_NO_INIT )
 #warning Check for obsolete memebers
-          , _backuppath ("/var/adm/backup")
-          , _packagebackups(false)
-          , _warndirexists(false)
-      {
-        process = 0;
-        exit_code = -1;
-
-        // Some rpm versions are patched not to abort installation if
-        // symlink creation failed.
-        setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
-        sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
-      }
+    , _backuppath ("/var/adm/backup")
+    , _packagebackups(false)
+    , _warndirexists(false)
+{
+  process = 0;
+  exit_code = -1;
+  librpmDb::globalInit();
+  // Some rpm versions are patched not to abort installation if
+  // symlink creation failed.
+  setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
+  sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -274,57 +252,62 @@ namespace zypp
 //     METHOD NAME : RpmDb::~RpmDb
 //     METHOD TYPE : Destructor
 //
-      RpmDb::~RpmDb()
-      {
-        MIL << "~RpmDb()" << endl;
-        closeDatabase();
+RpmDb::~RpmDb()
+{
+  MIL << "~RpmDb()" << endl;
+  closeDatabase();
+  delete process;
+  MIL  << "~RpmDb() end" << endl;
+  sKeyRingReceiver.reset();
+}
+
+Date RpmDb::timestamp() const
+{
+  Date ts_rpm;
 
-        delete process;
-        delete &_packages;
-        MIL  << "~RpmDb() end" << endl;
-        sKeyRingReceiver.reset();
-      }
+  Pathname db_path;
+  if ( dbPath().empty() )
+    db_path = "/var/lib/rpm";
+  else
+    db_path = dbPath();
 
-      Date RpmDb::timestamp() const
-      {
-        Date ts_rpm;
-        PathInfo rpmdb_info(root() + "/var/lib/rpm/Packages");
-        
-        if ( rpmdb_info.isExist() )
-          ts_rpm = rpmdb_info.mtime();
-        else
-          return Date::now();
-      }
+  PathInfo rpmdb_info(root() + db_path + "/Packages");
+
+  if ( rpmdb_info.isExist() )
+    return rpmdb_info.mtime();
+  else
+    return Date::now();
+}
 ///////////////////////////////////////////////////////////////////
 //
 //
 //     METHOD NAME : RpmDb::dumpOn
 //     METHOD TYPE : std::ostream &
 //
-      std::ostream & RpmDb::dumpOn( std::ostream & str ) const
-      {
-        str << "RpmDb[";
+std::ostream & RpmDb::dumpOn( std::ostream & str ) const
+{
+  str << "RpmDb[";
 
-        if ( _dbStateInfo == DbSI_NO_INIT )
-        {
-          str << "NO_INIT";
-        }
-        else
-        {
+  if ( _dbStateInfo == DbSI_NO_INIT )
+  {
+    str << "NO_INIT";
+  }
+  else
+  {
 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
-          str << "V4(";
-          ENUM_OUT( DbSI_HAVE_V4,      'X' );
-          ENUM_OUT( DbSI_MADE_V4,      'c' );
-          ENUM_OUT( DbSI_MODIFIED_V4,  'm' );
-          str << ")V3(";
-          ENUM_OUT( DbSI_HAVE_V3,      'X' );
-          ENUM_OUT( DbSI_HAVE_V3TOV4,  'B' );
-          ENUM_OUT( DbSI_MADE_V3TOV4,  'c' );
-          str << "): " << stringPath( _root, _dbPath );
+    str << "V4(";
+    ENUM_OUT( DbSI_HAVE_V4,    'X' );
+    ENUM_OUT( DbSI_MADE_V4,    'c' );
+    ENUM_OUT( DbSI_MODIFIED_V4,        'm' );
+    str << ")V3(";
+    ENUM_OUT( DbSI_HAVE_V3,    'X' );
+    ENUM_OUT( DbSI_HAVE_V3TOV4,        'B' );
+    ENUM_OUT( DbSI_MADE_V3TOV4,        'c' );
+    str << "): " << stringPath( _root, _dbPath );
 #undef ENUM_OUT
-        }
-        return str << "]";
-      }
+  }
+  return str << "]";
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -332,110 +315,125 @@ namespace zypp
 //     METHOD NAME : RpmDb::initDatabase
 //     METHOD TYPE : PMError
 //
-      void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
-      {
-        ///////////////////////////////////////////////////////////////////
-        // Check arguments
-        ///////////////////////////////////////////////////////////////////
-        if ( root_r.empty() )
-          root_r = "/";
+void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
+{
+  ///////////////////////////////////////////////////////////////////
+  // Check arguments
+  ///////////////////////////////////////////////////////////////////
+  bool quickinit( root_r.empty() );
 
-        if ( dbPath_r.empty() )
-          dbPath_r = "/var/lib/rpm";
+  if ( root_r.empty() )
+    root_r = "/";
 
-        if ( ! (root_r.absolute() && dbPath_r.absolute()) )
-        {
-          ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
-          ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
-        }
+  if ( dbPath_r.empty() )
+    dbPath_r = "/var/lib/rpm";
 
-        MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
+  if ( ! (root_r.absolute() && dbPath_r.absolute()) )
+  {
+    ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
+    ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
+  }
 
-        ///////////////////////////////////////////////////////////////////
-        // Check whether already initialized
-        ///////////////////////////////////////////////////////////////////
-        if ( initialized() )
-        {
-          if ( root_r == _root && dbPath_r == _dbPath )
-          {
-            return;
-          }
-          else
-          {
-            ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
-          }
-        }
+  if ( dbPath_r == "/var/lib/rpm"
+    && ! PathInfo( root_r/"/var/lib/rpm" ).isExist()
+    && PathInfo( root_r/"/usr/lib/sysimage/rpm" ).isDir() )
+  {
+    WAR << "Rpm package was deleted? Injecting missing rpmdb compat symlink." << endl;
+    filesystem::symlink( "../../usr/lib/sysimage/rpm", root_r/"/var/lib/rpm" );
+  }
+
+  MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r )
+      << ( doRebuild_r ? " (rebuilddb)" : "" )
+      << ( quickinit ? " (quickinit)" : "" ) << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Check whether already initialized
+  ///////////////////////////////////////////////////////////////////
+  if ( initialized() )
+  {
+    if ( root_r == _root && dbPath_r == _dbPath )
+    {
+      return;
+    }
+    else
+    {
+      ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
+    }
+  }
 
-        ///////////////////////////////////////////////////////////////////
-        // init database
-        ///////////////////////////////////////////////////////////////////
-        librpmDb::unblockAccess();
-        DbStateInfoBits info = DbSI_NO_INIT;
-        try
-        {
-          internal_initDatabase( root_r, dbPath_r, info );
-        }
-        catch (const RpmException & excpt_r)
-        {
-          ZYPP_CAUGHT(excpt_r);
-          librpmDb::blockAccess();
-          ERR << "Cleanup on error: state " << info << endl;
+  ///////////////////////////////////////////////////////////////////
+  // init database
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::unblockAccess();
 
-          if ( dbsi_has( info, DbSI_MADE_V4 ) )
-          {
-            // remove the newly created rpm4 database and
-            // any backup created on conversion.
-            removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
-          }
-          ZYPP_RETHROW(excpt_r);
-        }
-        if ( dbsi_has( info, DbSI_HAVE_V3 ) )
-        {
-          if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
-          {
-            // Move obsolete rpm3 database beside.
-            MIL << "Cleanup: state " << info << endl;
-            removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
-            dbsi_clr( info, DbSI_HAVE_V3 );
-          }
-          else
-          {
-            // Performing an update: Keep the original rpm3 database
-            // and wait if the rpm4 database gets modified by installing
-            // or removing packages. Cleanup in modifyDatabase or closeDatabase.
-            MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
-          }
-        }
+  if ( quickinit )
+  {
+    MIL << "QUICK initDatabase (no systemRoot set)" << endl;
+    return;
+  }
+
+  DbStateInfoBits info = DbSI_NO_INIT;
+  try
+  {
+    internal_initDatabase( root_r, dbPath_r, info );
+  }
+  catch (const RpmException & excpt_r)
+  {
+    ZYPP_CAUGHT(excpt_r);
+    librpmDb::blockAccess();
+    ERR << "Cleanup on error: state " << info << endl;
+
+    if ( dbsi_has( info, DbSI_MADE_V4 ) )
+    {
+      // remove the newly created rpm4 database and
+      // any backup created on conversion.
+      removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
+    }
+    ZYPP_RETHROW(excpt_r);
+  }
+  if ( dbsi_has( info, DbSI_HAVE_V3 ) )
+  {
+    if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
+    {
+      // Move obsolete rpm3 database beside.
+      MIL << "Cleanup: state " << info << endl;
+      removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
+      dbsi_clr( info, DbSI_HAVE_V3 );
+    }
+    else
+    {
+      // Performing an update: Keep the original rpm3 database
+      // and wait if the rpm4 database gets modified by installing
+      // or removing packages. Cleanup in modifyDatabase or closeDatabase.
+      MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
+    }
+  }
 #warning CHECK: notify root about conversion backup.
 
-        _root   = root_r;
-        _dbPath = dbPath_r;
-        _dbStateInfo = info;
+  _root   = root_r;
+  _dbPath = dbPath_r;
+  _dbStateInfo = info;
 
-#warning Add rebuild database once have the info about context
-#if 0
-        if ( ! ( Y2PM::runningFromSystem() ) )
-        {
-          if (      dbsi_has( info, DbSI_HAVE_V4 )
-                    && ! dbsi_has( info, DbSI_MADE_V4 ) )
-          {
-            err = rebuildDatabase();
-          }
-        }
-#endif
+  if ( doRebuild_r )
+  {
+    if (      dbsi_has( info, DbSI_HAVE_V4 )
+         && ! dbsi_has( info, DbSI_MADE_V4 ) )
+    {
+      rebuildDatabase();
+    }
+  }
 
-        MIL << "Syncronizing keys with zypp keyring" << std::endl;
-        importZyppKeyRingTrustedKeys();
-        exportTrustedKeysInZyppKeyRing();
+  MIL << "Synchronizing keys with zypp keyring" << endl;
+  syncTrustedKeys();
 
-        // Close the database in case any write acces (create/convert)
-        // happened during init. This should drop any lock acquired
-        // by librpm. On demand it will be reopened readonly and should
-        // not hold any lock.
-        librpmDb::dbRelease( true );
+  // Close the database in case any write acces (create/convert)
+  // happened during init. This should drop any lock acquired
+  // by librpm. On demand it will be reopened readonly and should
+  // not hold any lock.
+  librpmDb::dbRelease( true );
 
-        MIL << "InitDatabase: " << *this << endl;
-      }
+  MIL << "InitDatabase: " << *this << endl;
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -443,129 +441,124 @@ namespace zypp
 //     METHOD NAME : RpmDb::internal_initDatabase
 //     METHOD TYPE : PMError
 //
-      void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
-                                         DbStateInfoBits & info_r )
-      {
-        info_r = DbSI_NO_INIT;
-
-        ///////////////////////////////////////////////////////////////////
-        // Get info about the desired database dir
-        ///////////////////////////////////////////////////////////////////
-        librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
+void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
+                                   DbStateInfoBits & info_r )
+{
+  info_r = DbSI_NO_INIT;
 
-        if ( dbInfo.illegalArgs() )
-        {
-          // should not happen (checked in initDatabase)
-          ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
-        }
-        if ( ! dbInfo.usableArgs() )
-        {
-          ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
-          ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
-        }
+  ///////////////////////////////////////////////////////////////////
+  // Get info about the desired database dir
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
 
-        if ( dbInfo.hasDbV4() )
-        {
-          dbsi_set( info_r, DbSI_HAVE_V4 );
-          MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
-        }
-        else
-        {
-          MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
-        }
+  if ( dbInfo.illegalArgs() )
+  {
+    // should not happen (checked in initDatabase)
+    ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
+  }
+  if ( ! dbInfo.usableArgs() )
+  {
+    ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
+    ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
+  }
 
-        if ( dbInfo.hasDbV3() )
-        {
-          dbsi_set( info_r, DbSI_HAVE_V3 );
-        }
-        if ( dbInfo.hasDbV3ToV4() )
-        {
-          dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
-        }
+  if ( dbInfo.hasDbV4() )
+  {
+    dbsi_set( info_r, DbSI_HAVE_V4 );
+    MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
+  }
+  else
+  {
+    MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
+  }
 
-        DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
-        librpmDb::dumpState( DBG ) << endl;
+  if ( dbInfo.hasDbV3() )
+  {
+    dbsi_set( info_r, DbSI_HAVE_V3 );
+  }
+  if ( dbInfo.hasDbV3ToV4() )
+  {
+    dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
+  }
 
-        ///////////////////////////////////////////////////////////////////
-        // Access database, create if needed
-        ///////////////////////////////////////////////////////////////////
+  DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+  librpmDb::dumpState( DBG ) << endl;
 
-        // creates dbdir and empty rpm4 database if not present
-        librpmDb::dbAccess( root_r, dbPath_r );
+  ///////////////////////////////////////////////////////////////////
+  // Access database, create if needed
+  ///////////////////////////////////////////////////////////////////
 
-        if ( ! dbInfo.hasDbV4() )
-        {
-          dbInfo.restat();
-          if ( dbInfo.hasDbV4() )
-          {
-            dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
-          }
-        }
+  // creates dbdir and empty rpm4 database if not present
+  librpmDb::dbAccess( root_r, dbPath_r );
 
-        DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
-        librpmDb::dumpState( DBG ) << endl;
-
-        ///////////////////////////////////////////////////////////////////
-        // Check whether to convert something. Create backup but do
-        // not remove anything here
-        ///////////////////////////////////////////////////////////////////
-        librpmDb::constPtr dbptr;
-        librpmDb::dbAccess( dbptr );
-        bool dbEmpty = dbptr->empty();
-        if ( dbEmpty )
-        {
-          MIL << "Empty rpm4 database "  << dbInfo.dbV4() << endl;
-        }
+  if ( ! dbInfo.hasDbV4() )
+  {
+    dbInfo.restat();
+    if ( dbInfo.hasDbV4() )
+    {
+      dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
+    }
+  }
+
+  DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+  librpmDb::dumpState( DBG ) << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Check whether to convert something. Create backup but do
+  // not remove anything here
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::constPtr dbptr;
+  librpmDb::dbAccess( dbptr );
+  bool dbEmpty = dbptr->empty();
+  if ( dbEmpty )
+  {
+    MIL << "Empty rpm4 database "  << dbInfo.dbV4() << endl;
+  }
 
-        if ( dbInfo.hasDbV3() )
-        {
-          MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
+  if ( dbInfo.hasDbV3() )
+  {
+    MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
 
-          if ( dbEmpty )
-          {
-            extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
-            convertV3toV4( dbInfo.dbV3().path(), dbptr );
-
-            // create a backup copy
-            int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
-            if ( res )
-            {
-              WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
-            }
-            else
-            {
-              dbInfo.restat();
-              if ( dbInfo.hasDbV3ToV4() )
-              {
-                MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
-                dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
-              }
-            }
+    if ( dbEmpty )
+    {
+      extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
+      convertV3toV4( dbInfo.dbV3().path(), dbptr );
 
-          }
-          else
-          {
+      // create a backup copy
+      int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
+      if ( res )
+      {
+        WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
+      }
+      else
+      {
+        dbInfo.restat();
+        if ( dbInfo.hasDbV3ToV4() )
+        {
+          MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
+          dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
+        }
+      }
 
-            WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
-#warning EXCEPTION: nonempty rpm4 and rpm3 database found.
-            //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
-            //report->start( dbInfo.dbV3().path() );
-            //report->stop( some error );
+    }
+    else
+    {
 
-            // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
-            dbsi_set( info_r, DbSI_MODIFIED_V4 );
+      WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
+      // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
+      dbsi_set( info_r, DbSI_MODIFIED_V4 );
 
-          }
+    }
 
-          DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
-          librpmDb::dumpState( DBG ) << endl;
-        }
+    DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+    librpmDb::dumpState( DBG ) << endl;
+  }
 
-        if ( dbInfo.hasDbV3ToV4() )
-        {
-          MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
-        }
-      }
+  if ( dbInfo.hasDbV3ToV4() )
+  {
+    MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
+  }
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -573,65 +566,65 @@ namespace zypp
 //     METHOD NAME : RpmDb::removeV4
 //     METHOD TYPE : void
 //
-      void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
-      {
-        const char * v3backup = "packages.rpm3";
-        const char * master = "Packages";
-        const char * index[] =
-          {
-            "Basenames",
-            "Conflictname",
-            "Depends",
-            "Dirnames",
-            "Filemd5s",
-            "Group",
-            "Installtid",
-            "Name",
-            "Providename",
-            "Provideversion",
-            "Pubkeys",
-            "Requirename",
-            "Requireversion",
-            "Sha1header",
-            "Sigmd5",
-            "Triggername",
-            // last entry!
-            NULL
-          };
-
-        PathInfo pi( dbdir_r );
-        if ( ! pi.isDir() )
-        {
-          ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
-          return;
-        }
+void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
+{
+  const char * v3backup = "packages.rpm3";
+  const char * master = "Packages";
+  const char * index[] =
+    {
+      "Basenames",
+      "Conflictname",
+      "Depends",
+      "Dirnames",
+      "Filemd5s",
+      "Group",
+      "Installtid",
+      "Name",
+      "Providename",
+      "Provideversion",
+      "Pubkeys",
+      "Requirename",
+      "Requireversion",
+      "Sha1header",
+      "Sigmd5",
+      "Triggername",
+      // last entry!
+      NULL
+    };
+
+  PathInfo pi( dbdir_r );
+  if ( ! pi.isDir() )
+  {
+    ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
+    return;
+  }
 
-        for ( const char ** f = index; *f; ++f )
-        {
-          pi( dbdir_r + *f );
-          if ( pi.isFile() )
-          {
-            filesystem::unlink( pi.path() );
-          }
-        }
+  for ( const char ** f = index; *f; ++f )
+  {
+    pi( dbdir_r + *f );
+    if ( pi.isFile() )
+    {
+      filesystem::unlink( pi.path() );
+    }
+  }
 
-        pi( dbdir_r + master );
-        if ( pi.isFile() )
-        {
-          MIL << "Removing rpm4 database " << pi << endl;
-          filesystem::unlink( pi.path() );
-        }
+  pi( dbdir_r + master );
+  if ( pi.isFile() )
+  {
+    MIL << "Removing rpm4 database " << pi << endl;
+    filesystem::unlink( pi.path() );
+  }
 
-        if ( v3backup_r )
-        {
-          pi( dbdir_r + v3backup );
-          if ( pi.isFile() )
-          {
-            MIL << "Removing converted rpm3 database backup " << pi << endl;
-            filesystem::unlink( pi.path() );
-          }
-        }
-      }
+  if ( v3backup_r )
+  {
+    pi( dbdir_r + v3backup );
+    if ( pi.isFile() )
+    {
+      MIL << "Removing converted rpm3 database backup " << pi << endl;
+      filesystem::unlink( pi.path() );
+    }
+  }
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -639,65 +632,65 @@ namespace zypp
 //     METHOD NAME : RpmDb::removeV3
 //     METHOD TYPE : void
 //
-      void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
-      {
-        const char * master = "packages.rpm";
-        const char * index[] =
-          {
-            "conflictsindex.rpm",
-            "fileindex.rpm",
-            "groupindex.rpm",
-            "nameindex.rpm",
-            "providesindex.rpm",
-            "requiredby.rpm",
-            "triggerindex.rpm",
-            // last entry!
-            NULL
-          };
-
-        PathInfo pi( dbdir_r );
-        if ( ! pi.isDir() )
-        {
-          ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
-          return;
-        }
+void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
+{
+  const char * master = "packages.rpm";
+  const char * index[] =
+    {
+      "conflictsindex.rpm",
+      "fileindex.rpm",
+      "groupindex.rpm",
+      "nameindex.rpm",
+      "providesindex.rpm",
+      "requiredby.rpm",
+      "triggerindex.rpm",
+      // last entry!
+      NULL
+    };
+
+  PathInfo pi( dbdir_r );
+  if ( ! pi.isDir() )
+  {
+    ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
+    return;
+  }
 
-        for ( const char ** f = index; *f; ++f )
-        {
-          pi( dbdir_r + *f );
-          if ( pi.isFile() )
-          {
-            filesystem::unlink( pi.path() );
-          }
-        }
+  for ( const char ** f = index; *f; ++f )
+  {
+    pi( dbdir_r + *f );
+    if ( pi.isFile() )
+    {
+      filesystem::unlink( pi.path() );
+    }
+  }
 
 #warning CHECK: compare vs existing v3 backup. notify root
-        pi( dbdir_r + master );
-        if ( pi.isFile() )
-        {
-          Pathname m( pi.path() );
-          if ( v3backup_r )
-          {
-            // backup was already created
-            filesystem::unlink( m );
-            Pathname b( m.extend( "3" ) );
-            pi( b ); // stat backup
-          }
-          else
-          {
-            Pathname b( m.extend( ".deleted" ) );
-            pi( b );
-            if ( pi.isFile() )
-            {
-              // rempve existing backup
-              filesystem::unlink( b );
-            }
-            filesystem::rename( m, b );
-            pi( b ); // stat backup
-          }
-          MIL << "(Re)moved rpm3 database to " << pi << endl;
-        }
+  pi( dbdir_r + master );
+  if ( pi.isFile() )
+  {
+    Pathname m( pi.path() );
+    if ( v3backup_r )
+    {
+      // backup was already created
+      filesystem::unlink( m );
+      Pathname b( m.extend( "3" ) );
+      pi( b ); // stat backup
+    }
+    else
+    {
+      Pathname b( m.extend( ".deleted" ) );
+      pi( b );
+      if ( pi.isFile() )
+      {
+        // rempve existing backup
+        filesystem::unlink( b );
       }
+      filesystem::rename( m, b );
+      pi( b ); // stat backup
+    }
+    MIL << "(Re)moved rpm3 database to " << pi << endl;
+  }
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -705,25 +698,22 @@ namespace zypp
 //     METHOD NAME : RpmDb::modifyDatabase
 //     METHOD TYPE : void
 //
-      void RpmDb::modifyDatabase()
-      {
-        if ( ! initialized() )
-          return;
-
-        // tag database as modified
-        dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
+void RpmDb::modifyDatabase()
+{
+  if ( ! initialized() )
+    return;
 
-        // Move outdated rpm3 database beside.
-        if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
-        {
-          MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
-          removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
-          dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
-        }
+  // tag database as modified
+  dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
 
-        // invalidate Packages list
-        _packages._valid = false;
-      }
+  // Move outdated rpm3 database beside.
+  if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
+  {
+    MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
+    removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
+    dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
+  }
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -731,47 +721,46 @@ namespace zypp
 //     METHOD NAME : RpmDb::closeDatabase
 //     METHOD TYPE : PMError
 //
-      void RpmDb::closeDatabase()
-      {
-        if ( ! initialized() )
-        {
-          return;
-        }
+void RpmDb::closeDatabase()
+{
+  if ( ! initialized() )
+  {
+    return;
+  }
 
-        MIL << "Calling closeDatabase: " << *this << endl;
+  MIL << "Calling closeDatabase: " << *this << endl;
 
-        ///////////////////////////////////////////////////////////////////
-        // Block further database access
-        ///////////////////////////////////////////////////////////////////
-        _packages.clear();
-        librpmDb::blockAccess();
+  ///////////////////////////////////////////////////////////////////
+  // Block further database access
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::blockAccess();
 
-        ///////////////////////////////////////////////////////////////////
-        // Check fate if old version database still present
-        ///////////////////////////////////////////////////////////////////
-        if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
-        {
-          MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
-          if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
-          {
-            // Move outdated rpm3 database beside.
-            removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 )  );
-          }
-          else
-          {
-            // Remove unmodified rpm4 database
-            removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
-          }
-        }
+  ///////////////////////////////////////////////////////////////////
+  // Check fate if old version database still present
+  ///////////////////////////////////////////////////////////////////
+  if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
+  {
+    MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
+    if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
+    {
+      // Move outdated rpm3 database beside.
+      removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 )  );
+    }
+    else
+    {
+      // Remove unmodified rpm4 database
+      removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
+    }
+  }
 
-        ///////////////////////////////////////////////////////////////////
-        // Uninit
-        ///////////////////////////////////////////////////////////////////
-        _root = _dbPath = Pathname();
-        _dbStateInfo = DbSI_NO_INIT;
+  ///////////////////////////////////////////////////////////////////
+  // Uninit
+  ///////////////////////////////////////////////////////////////////
+  _root = _dbPath = Pathname();
+  _dbStateInfo = DbSI_NO_INIT;
 
-        MIL << "closeDatabase: " << *this << endl;
-      }
+  MIL << "closeDatabase: " << *this << endl;
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -779,493 +768,547 @@ namespace zypp
 //     METHOD NAME : RpmDb::rebuildDatabase
 //     METHOD TYPE : PMError
 //
-      void RpmDb::rebuildDatabase()
-      {
-        callback::SendReport<RebuildDBReport> report;
-
-        report->start( root() + dbPath() );
-
-        try
-        {
-          doRebuildDatabase(report);
-        }
-        catch (RpmException & excpt_r)
-        {
-          report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserString());
-          ZYPP_RETHROW(excpt_r);
-        }
-        report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
-      }
-
-      void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
-      {
-        FAILIFNOTINITIALIZED;
+void RpmDb::rebuildDatabase()
+{
+  callback::SendReport<RebuildDBReport> report;
 
-        MIL << "RpmDb::rebuildDatabase" << *this << endl;
-// FIXME  Timecount _t( "RpmDb::rebuildDatabase" );
+  report->start( root() + dbPath() );
 
-        PathInfo dbMaster( root() + dbPath() + "Packages" );
-        PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
+  try
+  {
+    doRebuildDatabase(report);
+  }
+  catch (RpmException & excpt_r)
+  {
+    report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserHistory());
+    ZYPP_RETHROW(excpt_r);
+  }
+  report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
+}
 
-        // run rpm
-        RpmArgVec opts;
-        opts.push_back("--rebuilddb");
-        opts.push_back("-vv");
+void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
+{
+  FAILIFNOTINITIALIZED;
 
-        // don't call modifyDatabase because it would remove the old
-        // rpm3 database, if the current database is a temporary one.
-        // But do invalidate packages list.
-        _packages._valid = false;
-        run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+  MIL << "RpmDb::rebuildDatabase" << *this << endl;
+  // FIXME  Timecount _t( "RpmDb::rebuildDatabase" );
 
-        // progress report: watch this file growing
-        PathInfo newMaster( root()
-                            + dbPath().extend( str::form( "rebuilddb.%d",
-                                                          process?process->getpid():0) )
-                            + "Packages" );
+  PathInfo dbMaster( root() + dbPath() + "Packages" );
+  PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
 
-        string       line;
-        string       errmsg;
+  // run rpm
+  RpmArgVec opts;
+  opts.push_back("--rebuilddb");
+  opts.push_back("-vv");
 
-        while ( systemReadLine( line ) )
-        {
-          if ( newMaster() )
-          { // file is removed at the end of rebuild.
-            // current size should be upper limit for new db
-            report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath());
-          }
+  // don't call modifyDatabase because it would remove the old
+  // rpm3 database, if the current database is a temporary one.
+  run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
 
-          if ( line.compare( 0, 2, "D:" ) )
-          {
-            errmsg += line + '\n';
-//      report.notify( line );
-            WAR << line << endl;
-          }
-        }
+  // progress report: watch this file growing
+  PathInfo newMaster( root()
+                      + dbPath().extend( str::form( "rebuilddb.%d",
+                                                    process?process->getpid():0) )
+                      + "Packages" );
 
-        int rpm_status = systemStatus();
+  std::string       line;
+  std::string       errmsg;
 
-        if ( rpm_status != 0 )
-        {
-          ZYPP_THROW(RpmSubprocessException(string("rpm failed with message: ") + errmsg));
-        }
-        else
-        {
-          report->progress( 100, root() + dbPath() ); // 100%
-        }
+  while ( systemReadLine( line ) )
+  {
+    if ( newMaster() )
+    { // file is removed at the end of rebuild.
+      // current size should be upper limit for new db
+      if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
+      {
+        WAR << "User requested abort." << endl;
+        systemKill();
+        filesystem::recursive_rmdir( newMaster.path().dirname() );
       }
+    }
 
-      void RpmDb::exportTrustedKeysInZyppKeyRing()
-      {
-        MIL << "Exporting rpm keyring into zypp trusted keyring" <<std::endl;
+    if ( line.compare( 0, 2, "D:" ) )
+    {
+      errmsg += line + '\n';
+      //      report.notify( line );
+      WAR << line << endl;
+    }
+  }
 
-        std::set<Edition> rpm_keys = pubkeyEditions();
+  int rpm_status = systemStatus();
 
-        std::list<PublicKey> zypp_keys;
-        zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
+  if ( rpm_status != 0 )
+  {
+    //TranslatorExplanation after semicolon is error message
+    ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
+  }
+  else
+  {
+    report->progress( 100, root() + dbPath() ); // 100%
+  }
+}
 
-        for ( std::set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
-          {
-            // search the zypp key into the rpm keys
-            // long id is edition version + release
-            std::string id = str::toUpper( (*it).version() + (*it).release());
-            std::list<PublicKey>::iterator ik = find( zypp_keys.begin(), zypp_keys.end(), id);
-            if ( ik != zypp_keys.end() )
-            {
-              MIL << "Key " << (*it) << " is already in zypp database." << std::endl;
-            }
-            else
-            {
-              // we export the rpm key into a file
-              RpmHeader::constPtr result = new RpmHeader();
-              getData( std::string("gpg-pubkey"), *it, result );
-              TmpFile file(getZYpp()->tmpPath());
-              std::ofstream os;
-              try
-              {
-                os.open(file.path().asString().c_str());
-                // dump rpm key into the tmp file
-                os << result->tag_description();
-                //MIL << "-----------------------------------------------" << std::endl;
-                //MIL << result->tag_description() <<std::endl;
-                //MIL << "-----------------------------------------------" << std::endl;
-                os.close();
-              }
-              catch (std::exception &e)
-              {
-                ERR << "Could not dump key " << (*it) << " in tmp file " << file.path() << std::endl;
-                // just ignore the key
-              }
-
-              // now import the key in zypp
-              try
-              {
-                getZYpp()->keyRing()->importKey( file.path(), true /*trusted*/);
-                MIL << "Trusted key " << (*it) << " imported in zypp keyring." << std::endl;
-              }
-              catch (Exception &e)
-              {
-                ERR << "Could not import key " << (*it) << " in zypp keyring" << std::endl;
-              }
-            }
-          }
+///////////////////////////////////////////////////////////////////
+namespace
+{
+  /** \ref RpmDb::syncTrustedKeys helper
+   * Compute which keys need to be exprted to / imported from the zypp keyring.
+   * Return result via argument list.
+   */
+  void computeKeyRingSync( std::set<Edition> & rpmKeys_r, std::list<PublicKeyData> & zyppKeys_r )
+  {
+    ///////////////////////////////////////////////////////////////////
+    // Remember latest release and where it ocurred
+    struct Key
+    {
+      Key()
+       : _inRpmKeys( nullptr )
+       , _inZyppKeys( nullptr )
+      {}
+
+      void updateIf( const Edition & rpmKey_r )
+      {
+       std::string keyRelease( rpmKey_r.release() );
+       int comp = _release.compare( keyRelease );
+       if ( comp < 0 )
+       {
+         // update to newer release
+         _release.swap( keyRelease );
+         _inRpmKeys  = &rpmKey_r;
+         _inZyppKeys = nullptr;
+         if ( !keyRelease.empty() )
+           DBG << "Old key in Z: gpg-pubkey-" << rpmKey_r.version() << "-" <<  keyRelease << endl;
+       }
+       else if ( comp == 0 )
+       {
+         // stay with this release
+         if ( ! _inRpmKeys )
+           _inRpmKeys = &rpmKey_r;
+       }
+       // else: this is an old release
+       else
+         DBG << "Old key in R: gpg-pubkey-" << rpmKey_r.version() << "-" <<  keyRelease << endl;
       }
 
-      void RpmDb::importZyppKeyRingTrustedKeys()
+      void updateIf( const PublicKeyData & zyppKey_r )
       {
-        MIL << "Importing zypp trusted keyring" << std::endl;
+       std::string keyRelease( zyppKey_r.gpgPubkeyRelease() );
+       int comp = _release.compare( keyRelease );
+       if ( comp < 0 )
+       {
+         // update to newer release
+         _release.swap( keyRelease );
+         _inRpmKeys  = nullptr;
+         _inZyppKeys = &zyppKey_r;
+         if ( !keyRelease.empty() )
+           DBG << "Old key in R: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
+       }
+       else if ( comp == 0 )
+       {
+         // stay with this release
+         if ( ! _inZyppKeys )
+           _inZyppKeys = &zyppKey_r;
+       }
+       // else: this is an old release
+       else
+         DBG << "Old key in Z: gpg-pubkey-" << zyppKey_r.gpgPubkeyVersion() << "-" << keyRelease << endl;
+      }
 
-        std::list<PublicKey> rpm_keys = pubkeys();
+      std::string _release;
+      const Edition * _inRpmKeys;
+      const PublicKeyData * _inZyppKeys;
+    };
+    ///////////////////////////////////////////////////////////////////
 
-        std::list<PublicKey> zypp_keys;
+    // collect keys by ID(version) and latest creation(release)
+    std::map<std::string,Key> _keymap;
 
-        zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
+    for_( it, rpmKeys_r.begin(), rpmKeys_r.end() )
+    {
+      _keymap[(*it).version()].updateIf( *it );
+    }
 
-        for ( std::list<PublicKey>::const_iterator it = zypp_keys.begin(); it != zypp_keys.end(); ++it)
-        {
-          // we find only the left part of the long gpg key, as rpm does not support long ids
-          std::list<PublicKey>::iterator ik = find( rpm_keys.begin(), rpm_keys.end(), (*it));
-          if ( ik != rpm_keys.end() )
-          {
-            MIL << "Key " << (*it).id() << " (" << (*it).name() << ") is already in rpm database." << std::endl;
-          }
-          else
-          {
-            // now import the key in rpm
-            try
-            {
-              importPubkey((*it).path());
-              MIL << "Trusted key " << (*it).id() << " (" << (*it).name() << ") imported in rpm database." << std::endl;
-            }
-            catch (RpmException &e)
-            {
-              ERR << "Could not import key " << (*it).id() << " (" << (*it).name() << " from " << (*it).path() << " in rpm database" << std::endl;
-            }
-          }
-        }
-      }
+    for_( it, zyppKeys_r.begin(), zyppKeys_r.end() )
+    {
+      _keymap[(*it).gpgPubkeyVersion()].updateIf( *it );
+    }
 
-///////////////////////////////////////////////////////////////////
-//
-//
-//     METHOD NAME : RpmDb::importPubkey
-//     METHOD TYPE : PMError
-//
-      void RpmDb::importPubkey( const Pathname & pubkey_r )
+    // compute missing keys
+    std::set<Edition> rpmKeys;
+    std::list<PublicKeyData> zyppKeys;
+    for_( it, _keymap.begin(), _keymap.end() )
+    {
+      DBG << "gpg-pubkey-" << (*it).first << "-" << (*it).second._release << " "
+          << ( (*it).second._inRpmKeys  ? "R" : "_" )
+         << ( (*it).second._inZyppKeys ? "Z" : "_" ) << endl;
+      if ( ! (*it).second._inRpmKeys )
+      {
+       zyppKeys.push_back( *(*it).second._inZyppKeys );
+      }
+      if ( ! (*it).second._inZyppKeys )
       {
-        FAILIFNOTINITIALIZED;
+       rpmKeys.insert( *(*it).second._inRpmKeys );
+      }
+    }
+    rpmKeys_r.swap( rpmKeys );
+    zyppKeys_r.swap( zyppKeys );
+  }
+} // namespace
+///////////////////////////////////////////////////////////////////
 
-        RpmArgVec opts;
-        opts.push_back ( "--import" );
-        opts.push_back ( "--" );
-        opts.push_back ( pubkey_r.asString().c_str() );
+void RpmDb::syncTrustedKeys( SyncTrustedKeyBits mode_r )
+{
+  MIL << "Going to sync trusted keys..." << endl;
+  std::set<Edition> rpmKeys( pubkeyEditions() );
+  std::list<PublicKeyData> zyppKeys( getZYpp()->keyRing()->trustedPublicKeyData() );
 
-        // don't call modifyDatabase because it would remove the old
-        // rpm3 database, if the current database is a temporary one.
-        // But do invalidate packages list.
-        _packages._valid = false;
-        run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+  if ( ! ( mode_r & SYNC_FROM_KEYRING ) )
+  {
+    // bsc#1064380: We relief PK from removing excess keys in the zypp keyring
+    // when re-acquiring the zyppp lock. For now we remove all excess keys.
+    // TODO: Once we can safely assume that all PK versions are updated we
+    // can think about re-importing newer key versions found in the zypp keyring and
+    // removing only excess ones (but case is not very likely). Unfixed PK versions
+    // however will remove the newer version found in the zypp keyring and by doing
+    // this, the key here will be removed via callback as well (keys are deleted
+    // via gpg id, regardless of the edition).
+    MIL << "Removing excess keys in zypp trusted keyring" << std::endl;
+    // Temporarily disconnect to prevent the attempt to pass back the delete request.
+    callback::TempConnect<KeyRingSignals> tempDisconnect;
+    bool dirty = false;
+    for ( const PublicKeyData & keyData : zyppKeys )
+    {
+      if ( ! rpmKeys.count( keyData.gpgPubkeyEdition() ) )
+      {
+       DBG << "Excess key in Z to delete: gpg-pubkey-" << keyData.gpgPubkeyEdition() << endl;
+       getZYpp()->keyRing()->deleteKey( keyData.id(), /*trusted*/true );
+       if ( !dirty ) dirty = true;
+      }
+    }
+    if ( dirty )
+      zyppKeys = getZYpp()->keyRing()->trustedPublicKeyData();
+  }
 
-        string line;
-        while ( systemReadLine( line ) )
-        {
-          if ( line.substr( 0, 6 ) == "error:" )
-          {
-            WAR << line << endl;
-          }
-          else
-          {
-            DBG << line << endl;
-          }
-        }
+  computeKeyRingSync( rpmKeys, zyppKeys );
+  MIL << (mode_r & SYNC_TO_KEYRING   ? "" : "(skip) ") << "Rpm keys to export into zypp trusted keyring: " << rpmKeys.size() << endl;
+  MIL << (mode_r & SYNC_FROM_KEYRING ? "" : "(skip) ") << "Zypp trusted keys to import into rpm database: " << zyppKeys.size() << endl;
 
-        int rpm_status = systemStatus();
+  ///////////////////////////////////////////////////////////////////
+  if ( (mode_r & SYNC_TO_KEYRING) &&  ! rpmKeys.empty() )
+  {
+    // export to zypp keyring
+    MIL << "Exporting rpm keyring into zypp trusted keyring" <<endl;
+    // Temporarily disconnect to prevent the attempt to re-import the exported keys.
+    callback::TempConnect<KeyRingSignals> tempDisconnect;
+    librpmDb::db_const_iterator keepDbOpen; // just to keep a ref.
 
-        if ( rpm_status != 0 )
-        {
-          ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.asString() + string(": rpm returned  ") + str::numstring(rpm_status)));
-        }
-        else
-        {
-          MIL << "Imported public key from file " << pubkey_r << endl;
-        }
+    TmpFile tmpfile( getZYpp()->tmpPath() );
+    {
+      std::ofstream tmpos( tmpfile.path().c_str() );
+      for_( it, rpmKeys.begin(), rpmKeys.end() )
+      {
+       // we export the rpm key into a file
+       RpmHeader::constPtr result;
+       getData( "gpg-pubkey", *it, result );
+       tmpos << result->tag_description() << endl;
       }
-
-///////////////////////////////////////////////////////////////////
-//
-//
-//     METHOD NAME : RpmDb::pubkeys
-//     METHOD TYPE : set<Edition>
-//
-      list<PublicKey> RpmDb::pubkeys() const
+    }
+    try
+    {
+      getZYpp()->keyRing()->multiKeyImport( tmpfile.path(), true /*trusted*/);
+      // bsc#1096217: Try to spot and report legacy V3 keys found in the rpm database.
+      // Modern rpm does not import those keys, but when migrating a pre SLE12 system
+      // we may find them. rpm>4.13 even complains on sderr if sucha key is present.
+      std::set<Edition> missingKeys;
+      for ( const Edition & key : rpmKeys )
       {
-        list<PublicKey> ret;
-
-        librpmDb::db_const_iterator it;
-        for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
-        {
-          Edition edition = it->tag_edition();
-          if (edition != Edition::noedition)
-          {
-            // we export the rpm key into a file
-            RpmHeader::constPtr result = new RpmHeader();
-            getData( std::string("gpg-pubkey"), edition, result );
-            TmpFile file(getZYpp()->tmpPath());
-            std::ofstream os;
-            try
-            {
-              os.open(file.path().asString().c_str());
-              // dump rpm key into the tmp file
-              os << result->tag_description();
-              //MIL << "-----------------------------------------------" << std::endl;
-              //MIL << result->tag_description() <<std::endl;
-              //MIL << "-----------------------------------------------" << std::endl;
-              os.close();
-              // read the public key from the dumped file
-              PublicKey key(file.path());
-              ret.push_back(key);
-            }
-            catch (std::exception &e)
-            {
-              ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << std::endl;
-              // just ignore the key
-            }
-          }
-        }
-        return ret;
+       if ( getZYpp()->keyRing()->isKeyTrusted( key.version() ) ) // key.version is the gpgkeys short ID
+         continue;
+       ERR << "Could not import key:" << str::Format("gpg-pubkey-%s") % key << " into zypp keyring (V3 key?)" << endl;
+       missingKeys.insert( key );
       }
+      if ( ! missingKeys.empty() )
+        callback::SendReport<KeyRingReport>()->reportNonImportedKeys(missingKeys);
+    }
+    catch ( const Exception & excpt )
+    {
+      ZYPP_CAUGHT( excpt );
+      ERR << "Could not import keys into zypp keyring: " << endl;
+    }
+  }
 
-      set<Edition> RpmDb::pubkeyEditions() const
-        {
-          set<Edition> ret;
-
-          librpmDb::db_const_iterator it;
-          for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
-          {
-            Edition edition = it->tag_edition();
-            if (edition != Edition::noedition)
-              ret.insert( edition );
-          }
-          return ret;
-        }
-
-///////////////////////////////////////////////////////////////////
-//
-//
-//     METHOD NAME : RpmDb::packagesValid
-//     METHOD TYPE : bool
-//
-      bool RpmDb::packagesValid() const
+  ///////////////////////////////////////////////////////////////////
+  if ( (mode_r & SYNC_FROM_KEYRING) && ! zyppKeys.empty() )
+  {
+    // import from zypp keyring
+    MIL << "Importing zypp trusted keyring" << std::endl;
+    for_( it, zyppKeys.begin(), zyppKeys.end() )
+    {
+      try
       {
-        return( _packages._valid || ! initialized() );
+       importPubkey( getZYpp()->keyRing()->exportTrustedPublicKey( *it ) );
       }
+      catch ( const RpmException & exp )
+      {
+       ZYPP_CAUGHT( exp );
+      }
+    }
+  }
+  MIL << "Trusted keys synced." << endl;
+}
+
+void RpmDb::importZyppKeyRingTrustedKeys()
+{ syncTrustedKeys( SYNC_FROM_KEYRING ); }
+
+void RpmDb::exportTrustedKeysInZyppKeyRing()
+{ syncTrustedKeys( SYNC_TO_KEYRING ); }
 
 ///////////////////////////////////////////////////////////////////
 //
 //
-//     METHOD NAME : RpmDb::getPackages
-//     METHOD TYPE : const std::list<Package::Ptr> &
-//
-//     DESCRIPTION :
+//     METHOD NAME : RpmDb::importPubkey
+//     METHOD TYPE : PMError
 //
-      const std::list<Package::Ptr> & RpmDb::getPackages()
-      {
-        callback::SendReport<ScanDBReport> report;
-
-        report->start ();
+void RpmDb::importPubkey( const PublicKey & pubkey_r )
+{
+  FAILIFNOTINITIALIZED;
 
-        try
-        {
-          const std::list<Package::Ptr> & ret = doGetPackages(report);
-          report->finish(ScanDBReport::NO_ERROR, "");
-          return ret;
-        }
-        catch (RpmException & excpt_r)
-        {
-          report->finish(ScanDBReport::FAILED, excpt_r.asUserString ());
-          ZYPP_RETHROW(excpt_r);
-        }
-#warning fixme
-        static const std::list<Package::Ptr> empty_list;
-        return empty_list;
-      }
+  // bnc#828672: On the fly key import in READONLY
+  if ( zypp_readonly_hack::IGotIt() )
+  {
+    WAR << "Key " << pubkey_r << " can not be imported. (READONLY MODE)" << endl;
+    return;
+  }
 
+  // check if the key is already in the rpm database
+  Edition keyEd( pubkey_r.gpgPubkeyVersion(), pubkey_r.gpgPubkeyRelease() );
+  std::set<Edition> rpmKeys = pubkeyEditions();
+  bool hasOldkeys = false;
 
-//
-// make Package::Ptr from RpmHeader
-// return NULL on error
-//
-      Package::Ptr RpmDb::makePackageFromHeader( const RpmHeader::constPtr header, std::set<std::string> * filerequires, const Pathname & location, Source_Ref source )
-      {
-        if ( ! header )
-          return 0;
+  for_( it, rpmKeys.begin(), rpmKeys.end() )
+  {
+    // bsc#1008325: Keys using subkeys for signing don't get a higher release
+    // if new subkeys are added, because the primary key remains unchanged.
+    // For now always re-import keys with subkeys. Here we don't want to export the
+    // keys in the rpm database to check whether the subkeys are the same. The calling
+    // code should take care, we don't re-import the same kesy over and over again.
+    if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
+    {
+      MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
+      return;
+    }
 
-        if ( header->isSrc() )
-        {
-          WAR << "Can't make Package from SourcePackage header" << endl;
-          return 0;
-        }
+    if ( keyEd.version() != (*it).version() )
+      continue; // different key ID (version)
 
-        Package::Ptr pptr;
+    if ( keyEd.release() < (*it).release() )
+    {
+      MIL << "Key " << pubkey_r << " is older than one in the rpm trusted keyring. (skip import)" << endl;
+      return;
+    }
+    else
+    {
+      hasOldkeys = true;
+    }
+  }
+  MIL << "Key " << pubkey_r << " will be imported into the rpm trusted keyring." << (hasOldkeys?"(update)":"(new)") << endl;
 
-        string name = header->tag_name();
+  if ( hasOldkeys )
+  {
+    // We must explicitly delete old key IDs first (all releases,
+    // that's why we don't call removePubkey here).
+    std::string keyName( "gpg-pubkey-" + keyEd.version() );
+    RpmArgVec opts;
+    opts.push_back ( "-e" );
+    opts.push_back ( "--allmatches" );
+    opts.push_back ( "--" );
+    opts.push_back ( keyName.c_str() );
+    // don't call modifyDatabase because it would remove the old
+    // rpm3 database, if the current database is a temporary one.
+    run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+    std::string line;
+    while ( systemReadLine( line ) )
+    {
+      ( str::startsWith( line, "error:" ) ? WAR : DBG ) << line << endl;
+    }
 
-        // create dataprovider
-        detail::ResImplTraits<RPMPackageImpl>::Ptr impl( new RPMPackageImpl( header ) );
+    if ( systemStatus() != 0 )
+    {
+      ERR << "Failed to remove key " << pubkey_r << " from RPM trusted keyring (ignored)" << endl;
+    }
+    else
+    {
+      MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
+    }
+  }
+
+  // import the new key
+  RpmArgVec opts;
+  opts.push_back ( "--import" );
+  opts.push_back ( "--" );
+  std::string pubkeypath( pubkey_r.path().asString() );
+  opts.push_back ( pubkeypath.c_str() );
+
+  // don't call modifyDatabase because it would remove the old
+  // rpm3 database, if the current database is a temporary one.
+  run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+  std::string line;
+  std::vector<std::string> excplines;
+  while ( systemReadLine( line ) )
+  {
+    if ( str::startsWith( line, "error:" ) )
+    {
+      WAR << line << endl;
+      excplines.push_back( std::move(line) );
+    }
+    else
+      DBG << line << endl;
+  }
+
+  if ( systemStatus() != 0 )
+  {
+    // Translator: %1% is a gpg public key
+    RpmSubprocessException excp( str::Format(_("Failed to import public key %1%") ) % pubkey_r.asString() );
+    excp.moveToHistory( excplines );
+    excp.addHistory( std::move(error_message) );
+    ZYPP_THROW( std::move(excp) );
+  }
+  else
+  {
+    MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
+  }
+}
 
-        impl->setSource( source );
-        if (!location.empty())
-          impl->setLocation( location );
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removePubkey
+//     METHOD TYPE : PMError
+//
+void RpmDb::removePubkey( const PublicKey & pubkey_r )
+{
+  FAILIFNOTINITIALIZED;
 
-        Edition edition;
-        try
-        {
-          edition = Edition( header->tag_version(),
-                             header->tag_release(),
-                             header->tag_epoch());
-        }
-        catch (Exception & excpt_r)
-        {
-          ZYPP_CAUGHT( excpt_r );
-          WAR << "Package " << name << " has bad edition '"
-          << (header->tag_epoch().empty()?"":(header->tag_epoch()+":"))
-          << header->tag_version()
-          << (header->tag_release().empty()?"":(string("-") + header->tag_release())) << "'";
-          return pptr;
-        }
+  // check if the key is in the rpm database and just
+  // return if it does not.
+  std::set<Edition> rpm_keys = pubkeyEditions();
+  std::set<Edition>::const_iterator found_edition = rpm_keys.end();
+  std::string pubkeyVersion( pubkey_r.gpgPubkeyVersion() );
 
-        Arch arch;
-        try
-        {
-          arch = Arch( header->tag_arch() );
-        }
-        catch (Exception & excpt_r)
-        {
-          ZYPP_CAUGHT( excpt_r );
-          WAR << "Package " << name << " has bad architecture '" << header->tag_arch() << "'";
-          return pptr;
-        }
+  for_( it, rpm_keys.begin(), rpm_keys.end() )
+  {
+    if ( (*it).version() == pubkeyVersion )
+    {
+       found_edition = it;
+       break;
+    }
+  }
 
-        // Collect basic Resolvable data
-        NVRAD dataCollect( header->tag_name(),
-                           edition,
-                           arch );
+  // the key does not exist, cannot be removed
+  if (found_edition == rpm_keys.end())
+  {
+      WAR << "Key " << pubkey_r.id() << " is not in rpm db" << endl;
+      return;
+  }
 
-        list<string> filenames = impl->filenames();
-        dataCollect[Dep::PROVIDES] = header->tag_provides ( filerequires );
-        CapFactory capfactory;
+  std::string rpm_name("gpg-pubkey-" + found_edition->asString());
 
-        static str::smatch what;
-        static const str::regex filenameRegex( "/(s?bin|lib(64)?|etc)/|^/usr/(games/|share/(dict/words|magic\\.mime)$)|^/opt/gnome/games/",
-                                               str::regex::optimize|str::regex::nosubs );
+  RpmArgVec opts;
+  opts.push_back ( "-e" );
+  opts.push_back ( "--" );
+  opts.push_back ( rpm_name.c_str() );
 
-        for (list<string>::const_iterator filename = filenames.begin();
-             filename != filenames.end();
-             ++filename)
-        {
-          if ( str::regex_search( filename->begin(), filename->end(), what, filenameRegex ) )
-          {
-            try
-            {
-              dataCollect[Dep::PROVIDES].insert( capfactory.parse(ResTraits<Package>::kind, *filename) );
-            }
-            catch (Exception & excpt_r)
-            {
-              ZYPP_CAUGHT( excpt_r );
-              WAR << "Ignoring invalid capability: " << *filename << endl;
-            }
-          }
-        }
+  // don't call modifyDatabase because it would remove the old
+  // rpm3 database, if the current database is a temporary one.
+  run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
 
-        dataCollect[Dep::REQUIRES]    = header->tag_requires( filerequires );
-        dataCollect[Dep::PREREQUIRES] = header->tag_prerequires( filerequires );
-        dataCollect[Dep::CONFLICTS]   = header->tag_conflicts( filerequires );
-        dataCollect[Dep::OBSOLETES]   = header->tag_obsoletes( filerequires );
-        dataCollect[Dep::ENHANCES]    = header->tag_enhances( filerequires );
-        dataCollect[Dep::SUPPLEMENTS] = header->tag_supplements( filerequires );
+  std::string line;
+  std::vector<std::string> excplines;
+  while ( systemReadLine( line ) )
+  {
+    if ( str::startsWith( line, "error:" ) )
+    {
+      WAR << line << endl;
+      excplines.push_back( std::move(line) );
+    }
+    else
+      DBG << line << endl;
+  }
+
+  if ( systemStatus() != 0 )
+  {
+    // Translator: %1% is a gpg public key
+    RpmSubprocessException excp( str::Format(_("Failed to remove public key %1%") ) % pubkey_r.asString() );
+    excp.moveToHistory( excplines );
+    excp.addHistory( std::move(error_message) );
+    ZYPP_THROW( std::move(excp) );
+  }
+  else
+  {
+    MIL << "Key " << pubkey_r << " has been removed from RPM trusted keyring" << endl;
+  }
+}
 
-        try
-        {
-          // create package from dataprovider
-          pptr = detail::makeResolvableFromImpl( dataCollect, impl );
-        }
-        catch (Exception & excpt_r)
-        {
-          ZYPP_CAUGHT( excpt_r );
-          ERR << "Can't create Package::Ptr" << endl;
-        }
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::pubkeys
+//     METHOD TYPE : std::set<Edition>
+//
+std::list<PublicKey> RpmDb::pubkeys() const
+{
+  std::list<PublicKey> ret;
 
-        return pptr;
+  librpmDb::db_const_iterator it;
+  for ( it.findByName( "gpg-pubkey" ); *it; ++it )
+  {
+    Edition edition = it->tag_edition();
+    if (edition != Edition::noedition)
+    {
+      // we export the rpm key into a file
+      RpmHeader::constPtr result;
+      getData( "gpg-pubkey", edition, result );
+      TmpFile file(getZYpp()->tmpPath());
+      std::ofstream os;
+      try
+      {
+        os.open(file.path().asString().c_str());
+        // dump rpm key into the tmp file
+        os << result->tag_description();
+        //MIL << "-----------------------------------------------" << endl;
+        //MIL << result->tag_description() <<endl;
+        //MIL << "-----------------------------------------------" << endl;
+        os.close();
+        // read the public key from the dumped file
+        PublicKey key(file);
+        ret.push_back(key);
       }
-
-
-      const std::list<Package::Ptr> & RpmDb::doGetPackages(callback::SendReport<ScanDBReport> & report)
+      catch ( std::exception & e )
       {
-        if ( packagesValid() )
-        {
-          return _packages._list;
-        }
-
-        _packages.clear();
-
-        ///////////////////////////////////////////////////////////////////
-        // Collect package data.
-        ///////////////////////////////////////////////////////////////////
-        unsigned expect = 0;
-        librpmDb::constPtr dbptr;
-        librpmDb::dbAccess( dbptr );
-        expect = dbptr->size();
-        DBG << "Expecting " << expect << " packages" << endl;
-
-        librpmDb::db_const_iterator iter;
-        unsigned current = 0;
-        CapFactory _f;
-        Pathname location;
-
-        for ( iter.findAll(); *iter; ++iter, ++current, report->progress( (100*current)/expect))
-        {
-
-          string name = iter->tag_name();
-          if ( name == string( "gpg-pubkey" ) )
-          {
-            DBG << "Ignoring pseudo package " << name << endl;
-            // pseudo package filtered, as we can't handle multiple instances
-            // of 'gpg-pubkey-VERS-REL'.
-            continue;
-          }
-          Date installtime = iter->tag_installtime();
-
-          Package::Ptr pptr = makePackageFromHeader( *iter, &_filerequires, location, Source_Ref() );
-
-          _packages._list.push_back( pptr );
-        }
-        _packages.buildIndex();
-        DBG << "Found installed packages: " << _packages._list.size() << endl;
-
-        ///////////////////////////////////////////////////////////////////
-        // Evaluate filerequires collected so far
-        ///////////////////////////////////////////////////////////////////
-        for ( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it )
-          {
+        ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
+        // just ignore the key
+      }
+    }
+  }
+  return ret;
+}
 
-            for ( iter.findByFile( *it ); *iter; ++iter )
-            {
-              Package::Ptr pptr = _packages.lookup( iter->tag_name() );
-              if ( !pptr )
-              {
-                WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
-                continue;
-              }
-              pptr->injectProvides(_f.parse(ResTraits<Package>::kind, *it));
-            }
+std::set<Edition> RpmDb::pubkeyEditions() const
+  {
+    std::set<Edition> ret;
 
-          }
+    librpmDb::db_const_iterator it;
+    for ( it.findByName( "gpg-pubkey" ); *it; ++it )
+    {
+      Edition edition = it->tag_edition();
+      if (edition != Edition::noedition)
+        ret.insert( edition );
+    }
+    return ret;
+  }
 
-        ///////////////////////////////////////////////////////////////////
-        // Build final packages list
-        ///////////////////////////////////////////////////////////////////
-        return _packages._list;
-      }
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1275,26 +1318,26 @@ namespace zypp
 //
 //     DESCRIPTION :
 //
-      std::list<FileInfo>
-      RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
-      {
-        std::list<FileInfo> result;
+std::list<FileInfo>
+RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
+{
+  std::list<FileInfo> result;
 
-        librpmDb::db_const_iterator it;
-        bool found;
-        if (edition_r == Edition::noedition)
-        {
-          found = it.findPackage( name_r );
-        }
-        else
-        {
-          found = it.findPackage( name_r, edition_r );
-        }
-        if (!found)
-          return result;
+  librpmDb::db_const_iterator it;
+  bool found;
+  if (edition_r == Edition::noedition)
+  {
+    found = it.findPackage( name_r );
+  }
+  else
+  {
+    found = it.findPackage( name_r, edition_r );
+  }
+  if (!found)
+    return result;
 
-        return result;
-      }
+  return result;
+}
 
 
 ///////////////////////////////////////////////////////////////////
@@ -1305,41 +1348,41 @@ namespace zypp
 //
 //     DESCRIPTION :
 //
-      bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
-      {
-        librpmDb::db_const_iterator it;
-        bool res;
-        do
-        {
-          res = it.findByFile( file_r );
-          if (!res) break;
-          if (!name_r.empty())
-          {
-            res = (it->tag_name() == name_r);
-          }
-          ++it;
-        }
-        while (res && *it);
-        return res;
-      }
+bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
+{
+  librpmDb::db_const_iterator it;
+  bool res;
+  do
+  {
+    res = it.findByFile( file_r );
+    if (!res) break;
+    if (!name_r.empty())
+    {
+      res = (it->tag_name() == name_r);
+    }
+    ++it;
+  }
+  while (res && *it);
+  return res;
+}
 
 ///////////////////////////////////////////////////////////////////
 //
 //
 //     METHOD NAME : RpmDb::whoOwnsFile
-//     METHOD TYPE : string
+//     METHOD TYPE : std::string
 //
 //     DESCRIPTION :
 //
-      std::string RpmDb::whoOwnsFile( const std::string & file_r) const
-      {
-        librpmDb::db_const_iterator it;
-        if (it.findByFile( file_r ))
-        {
-          return it->tag_name();
-        }
-        return "";
-      }
+std::string RpmDb::whoOwnsFile( const std::string & file_r) const
+{
+  librpmDb::db_const_iterator it;
+  if (it.findByFile( file_r ))
+  {
+    return it->tag_name();
+  }
+  return "";
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1349,11 +1392,11 @@ namespace zypp
 //
 //     DESCRIPTION :
 //
-      bool RpmDb::hasProvides( const std::string & tag_r ) const
-      {
-        librpmDb::db_const_iterator it;
-        return it.findByProvides( tag_r );
-      }
+bool RpmDb::hasProvides( const std::string & tag_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByProvides( tag_r );
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1363,11 +1406,11 @@ namespace zypp
 //
 //     DESCRIPTION :
 //
-      bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
-      {
-        librpmDb::db_const_iterator it;
-        return it.findByRequiredBy( tag_r );
-      }
+bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByRequiredBy( tag_r );
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1377,11 +1420,11 @@ namespace zypp
 //
 //     DESCRIPTION :
 //
-      bool RpmDb::hasConflicts( const std::string & tag_r ) const
-      {
-        librpmDb::db_const_iterator it;
-        return it.findByConflicts( tag_r );
-      }
+bool RpmDb::hasConflicts( const std::string & tag_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByConflicts( tag_r );
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1391,11 +1434,11 @@ namespace zypp
 //
 //     DESCRIPTION :
 //
-      bool RpmDb::hasPackage( const string & name_r ) const
-      {
-        librpmDb::db_const_iterator it;
-        return it.findPackage( name_r );
-      }
+bool RpmDb::hasPackage( const std::string & name_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findPackage( name_r );
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1405,11 +1448,11 @@ namespace zypp
 //
 //     DESCRIPTION :
 //
-      bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
-      {
-        librpmDb::db_const_iterator it;
-        return it.findPackage( name_r, ed_r );
-      }
+bool RpmDb::hasPackage( const std::string & name_r, const Edition & ed_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findPackage( name_r, ed_r );
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1419,414 +1462,508 @@ namespace zypp
 //
 //     DESCRIPTION :
 //
-      void RpmDb::getData( const string & name_r,
-                           RpmHeader::constPtr & result_r ) const
-      {
-        librpmDb::db_const_iterator it;
-        it.findPackage( name_r );
-        result_r = *it;
-        if (it.dbError())
-          ZYPP_THROW(*(it.dbError()));
-      }
+void RpmDb::getData( const std::string & name_r,
+                     RpmHeader::constPtr & result_r ) const
+{
+  librpmDb::db_const_iterator it;
+  it.findPackage( name_r );
+  result_r = *it;
+  if (it.dbError())
+    ZYPP_THROW(*(it.dbError()));
+}
 
 ///////////////////////////////////////////////////////////////////
 //
 //
 //     METHOD NAME : RpmDb::getData
-//     METHOD TYPE : PMError
+//     METHOD TYPE : void
 //
 //     DESCRIPTION :
 //
-      void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
-                           RpmHeader::constPtr & result_r ) const
-      {
-        librpmDb::db_const_iterator it;
-        it.findPackage( name_r, ed_r  );
-        result_r = *it;
-        if (it.dbError())
-          ZYPP_THROW(*(it.dbError()));
-      }
-
-      /*--------------------------------------------------------------*/
-      /* Checking the source rpm <rpmpath> with rpm --chcksig and     */
-      /* the version number.                                           */
-      /*--------------------------------------------------------------*/
-      unsigned
-      RpmDb::checkPackage (const Pathname & packagePath, string version, string md5 )
-      {
-        unsigned result = 0;
+void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
+                     RpmHeader::constPtr & result_r ) const
+{
+  librpmDb::db_const_iterator it;
+  it.findPackage( name_r, ed_r  );
+  result_r = *it;
+  if (it.dbError())
+    ZYPP_THROW(*(it.dbError()));
+}
 
-        if ( ! version.empty() )
-        {
-          RpmHeader::constPtr h( RpmHeader::readPackage( packagePath, RpmHeader::NOSIGNATURE ) );
-          if ( ! h || Edition( version ) != h->tag_edition() )
-          {
-            result |= CHK_INCORRECT_VERSION;
-          }
-        }
+///////////////////////////////////////////////////////////////////
+namespace
+{
+  struct RpmlogCapture : public std::string
+  {
+    RpmlogCapture()
+    { rpmlog()._cap = this; }
 
-        if (!md5.empty())
-        {
-#warning TBD MD5 check
-          WAR << "md5sum check not yet implemented" << endl;
-          return CHK_INCORRECT_FILEMD5;
-        }
+    ~RpmlogCapture()
+    { rpmlog()._cap = nullptr; }
 
-        std::string path = packagePath.asString();
-        // checking --checksig
-        const char *const argv[] =
-          {
-            "rpm", "--checksig", "--", path.c_str(), 0
-          };
+  private:
+    struct Rpmlog
+    {
+      Rpmlog()
+      : _cap( nullptr )
+      {
+       rpmlogSetCallback( rpmLogCB, this );
+       rpmSetVerbosity( RPMLOG_INFO );
+       _f = ::fopen( "/dev/null","w");
+       rpmlogSetFile( _f );
+      }
 
-        exit_code = -1;
+      ~Rpmlog()
+      { if ( _f ) ::fclose( _f ); }
 
-        string output = "";
-        unsigned int k;
-        for ( k = 0; k < (sizeof(argv) / sizeof(*argv)) -1; k++ )
-        {
-          output = output + " " + argv[k];
-        }
+      static int rpmLogCB( rpmlogRec rec_r, rpmlogCallbackData data_r )
+      { return reinterpret_cast<Rpmlog*>(data_r)->rpmLog( rec_r ); }
 
-        DBG << "rpm command: " << output << endl;
+      int rpmLog( rpmlogRec rec_r )
+      {
+       if ( _cap ) (*_cap) += rpmlogRecMessage( rec_r );
+       return RPMLOG_DEFAULT;
+      }
 
-        if ( process != NULL )
-        {
-          delete process;
-          process = NULL;
-        }
-        // Launch the program
-        process = new ExternalProgram( argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
+      FILE * _f;
+      std::string * _cap;
+    };
 
+    static Rpmlog & rpmlog()
+    { static Rpmlog _rpmlog; return _rpmlog; }
+  };
 
-        if ( process == NULL )
-        {
-          result |= CHK_OTHER_FAILURE;
-          DBG << "create process failed" << endl;
-        }
+  RpmDb::CheckPackageResult doCheckPackageSig( const Pathname & path_r,                        // rpm file to check
+                                              const Pathname & root_r,                 // target root
+                                              bool  requireGPGSig_r,                   // whether no gpg signature is to be reported
+                                              RpmDb::CheckPackageDetail & detail_r )   // detailed result
+  {
+    PathInfo file( path_r );
+    if ( ! file.isFile() )
+    {
+      ERR << "Not a file: " << file << endl;
+      return RpmDb::CHK_ERROR;
+    }
 
-        string value;
-        output = process->receiveLine();
+    FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
+    if ( fd == 0 || ::Ferror(fd) )
+    {
+      ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
+      if ( fd )
+       ::Fclose( fd );
+      return RpmDb::CHK_ERROR;
+    }
+    rpmts ts = ::rpmtsCreate();
+    ::rpmtsSetRootDir( ts, root_r.c_str() );
+    ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
+
+    rpmQVKArguments_s qva;
+    memset( &qva, 0, sizeof(rpmQVKArguments_s) );
+#ifdef HAVE_NO_RPMTSSETVFYFLAGS
+    // Legacy: In rpm >= 4.15 qva_flags symbols don't exist
+    // and qva_flags is not used in signature checking at all.
+    qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
+#else
+    ::rpmtsSetVfyFlags( ts, RPMVSF_DEFAULT );
+#endif
+    RpmlogCapture vresult;
+    LocaleGuard guard( LC_ALL, "C" );  // bsc#1076415: rpm log output is localized, but we need to parse it :(
+    int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
+    guard.restore();
+
+    ts = rpmtsFree(ts);
+    ::Fclose( fd );
+
+    // results per line...
+    //     Header V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
+    //     Header SHA1 digest: OK (a60386347863affefef484ff1f26c889373eb094)
+    //     V3 RSA/SHA256 Signature, key ID 3dbdc284: OK
+    //     MD5 digest: OK (fd5259fe677a406951dcb2e9d08c4dcc)
+    //
+    // TODO: try to get SIG info from the header rather than parsing the output
+    std::vector<std::string> lines;
+    str::split( vresult, std::back_inserter(lines), "\n" );
+    unsigned count[7] = { 0, 0, 0, 0, 0, 0, 0 };
+
+    for ( unsigned i = 1; i < lines.size(); ++i )
+    {
+      std::string & line( lines[i] );
+      RpmDb::CheckPackageResult lineres = RpmDb::CHK_ERROR;
+      if ( line.find( ": OK" ) != std::string::npos )
+      {
+       lineres = RpmDb::CHK_OK;
+       if ( line.find( "Signature, key ID" ) == std::string::npos )
+         ++count[RpmDb::CHK_NOSIG];    // Valid but no gpg signature -> CHK_NOSIG
+      }
+      else if ( line.find( ": NOKEY" ) != std::string::npos )
+      { lineres = RpmDb::CHK_NOKEY; }
+      else if ( line.find( ": BAD" ) != std::string::npos )
+      { lineres = RpmDb::CHK_FAIL; }
+      else if ( line.find( ": UNKNOWN" ) != std::string::npos )
+      { lineres = RpmDb::CHK_NOTFOUND; }
+      else if ( line.find( ": NOTRUSTED" ) != std::string::npos )
+      { lineres = RpmDb::CHK_NOTTRUSTED; }
 
-        while ( output.length() > 0)
-        {
-          string::size_type         ret;
+      ++count[lineres];
+      detail_r.push_back( RpmDb::CheckPackageDetail::value_type( lineres, std::move(line) ) );
+    }
 
-          // extract \n
-          ret = output.find_first_of ( "\n" );
-          if ( ret != string::npos )
-          {
-            value.assign ( output, 0, ret );
-          }
-          else
-          {
-            value = output;
-          }
+    RpmDb::CheckPackageResult ret = ( res ? RpmDb::CHK_ERROR : RpmDb::CHK_OK );
 
-          DBG << "stdout: " << value << endl;
+    if ( count[RpmDb::CHK_FAIL] )
+      ret = RpmDb::CHK_FAIL;
 
-          string::size_type pos;
-          if ((pos = value.find (path)) != string::npos)
-          {
-            string rest = value.substr (pos + path.length() + 1);
-            if (rest.find("NOT OK") == string::npos)
-            {
-              // see what checks are ok
-              if (rest.find("md5") == string::npos)
-              {
-                result |= CHK_MD5SUM_MISSING;
-              }
-              if (rest.find("gpg") == string::npos)
-              {
-                result |= CHK_GPGSIG_MISSING;
-              }
-            }
-            else
-            {
-              // see what checks are not ok
-              if (rest.find("MD5") != string::npos)
-              {
-                result |= CHK_INCORRECT_PKGMD5;
-              }
-              else
-              {
-                result |= CHK_MD5SUM_MISSING;
-              }
-
-              if (rest.find("GPG") != string::npos)
-              {
-                result |= CHK_INCORRECT_GPGSIG;
-              }
-              else
-              {
-                result |= CHK_GPGSIG_MISSING;
-              }
-            }
-          }
+    else if ( count[RpmDb::CHK_NOTFOUND] )
+      ret = RpmDb::CHK_NOTFOUND;
 
-          output = process->receiveLine();
-        }
+    else if ( count[RpmDb::CHK_NOKEY] )
+      ret = RpmDb::CHK_NOKEY;
 
-        if ( result == 0 && systemStatus() != 0 )
-        {
-          // error
-          result |= CHK_OTHER_FAILURE;
-        }
+    else if ( count[RpmDb::CHK_NOTTRUSTED] )
+      ret = RpmDb::CHK_NOTTRUSTED;
 
-        return ( result );
+    else if ( ret == RpmDb::CHK_OK )
+    {
+      if ( count[RpmDb::CHK_OK] == count[RpmDb::CHK_NOSIG]  )
+      {
+       detail_r.push_back( RpmDb::CheckPackageDetail::value_type( RpmDb::CHK_NOSIG, std::string("    ")+_("Package is not signed!") ) );
+       if ( requireGPGSig_r )
+         ret = RpmDb::CHK_NOSIG;
       }
+    }
 
-// determine changed files of installed package
-      bool
-      RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
-      {
-        bool ok = true;
+    if ( ret != RpmDb::CHK_OK )
+    {
+      WAR << path_r << " (" << requireGPGSig_r << " -> " << ret << ")" << endl;
+      WAR << vresult;
+    }
+    return ret;
+  }
 
-        fileList.clear();
+} // namespace
+///////////////////////////////////////////////////////////////////
+//
+//     METHOD NAME : RpmDb::checkPackage
+//     METHOD TYPE : RpmDb::CheckPackageResult
+//
+RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r, CheckPackageDetail & detail_r )
+{ return doCheckPackageSig( path_r, root(), false/*requireGPGSig_r*/, detail_r ); }
 
-        if ( ! initialized() ) return false;
+RpmDb::CheckPackageResult RpmDb::checkPackage( const Pathname & path_r )
+{ CheckPackageDetail dummy; return checkPackage( path_r, dummy ); }
 
-        RpmArgVec opts;
+RpmDb::CheckPackageResult RpmDb::checkPackageSignature( const Pathname & path_r, RpmDb::CheckPackageDetail & detail_r )
+{ return doCheckPackageSig( path_r, root(), true/*requireGPGSig_r*/, detail_r ); }
 
-        opts.push_back ("-V");
-        opts.push_back ("--nodeps");
-        opts.push_back ("--noscripts");
-        opts.push_back ("--nomd5");
-        opts.push_back ("--");
-        opts.push_back (packageName.c_str());
 
-        run_rpm (opts, ExternalProgram::Discard_Stderr);
+// determine changed files of installed package
+bool
+RpmDb::queryChangedFiles(FileList & fileList, const std::string& packageName)
+{
+  bool ok = true;
 
-        if ( process == NULL )
-          return false;
+  fileList.clear();
 
-        /* from rpm manpage
-           5      MD5 sum
-           S      File size
-           L      Symlink
-           T      Mtime
-           D      Device
-           U      User
-           G      Group
-           M      Mode (includes permissions and file type)
-        */
+  if ( ! initialized() ) return false;
 
-        string line;
-        while (systemReadLine(line))
-        {
-          if (line.length() > 12 &&
-              (line[0] == 'S' || line[0] == 's' ||
-               (line[0] == '.' && line[7] == 'T')))
-          {
-            // file has been changed
-            string filename;
+  RpmArgVec opts;
 
-            filename.assign(line, 11, line.length() - 11);
-            fileList.insert(filename);
-          }
-        }
+  opts.push_back ("-V");
+  opts.push_back ("--nodeps");
+  opts.push_back ("--noscripts");
+  opts.push_back ("--nomd5");
+  opts.push_back ("--");
+  opts.push_back (packageName.c_str());
 
-        systemStatus();
-        // exit code ignored, rpm returns 1 no matter if package is installed or
-        // not
+  run_rpm (opts, ExternalProgram::Discard_Stderr);
 
-        return ok;
-      }
+  if ( process == NULL )
+    return false;
 
+  /* from rpm manpage
+   5      MD5 sum
+   S      File size
+   L      Symlink
+   T      Mtime
+   D      Device
+   U      User
+   G      Group
+   M      Mode (includes permissions and file type)
+  */
 
+  std::string line;
+  while (systemReadLine(line))
+  {
+    if (line.length() > 12 &&
+        (line[0] == 'S' || line[0] == 's' ||
+         (line[0] == '.' && line[7] == 'T')))
+    {
+      // file has been changed
+      std::string filename;
 
-      /****************************************************************/
-      /* private member-functions                                      */
-      /****************************************************************/
+      filename.assign(line, 11, line.length() - 11);
+      fileList.insert(filename);
+    }
+  }
 
-      /*--------------------------------------------------------------*/
-      /* Run rpm with the specified arguments, handling stderr */
-      /* as specified  by disp                                 */
-      /*--------------------------------------------------------------*/
-      void
-      RpmDb::run_rpm (const RpmArgVec& opts,
-                      ExternalProgram::Stderr_Disposition disp)
-      {
-        if ( process )
-        {
-          delete process;
-          process = NULL;
-        }
-        exit_code = -1;
+  systemStatus();
+  // exit code ignored, rpm returns 1 no matter if package is installed or
+  // not
 
-        if ( ! initialized() )
-        {
-          ZYPP_THROW(RpmDbNotOpenException());
-        }
+  return ok;
+}
 
-        RpmArgVec args;
 
-        // always set root and dbpath
-        args.push_back("rpm");
-        args.push_back("--root");
-        args.push_back(_root.asString().c_str());
-        args.push_back("--dbpath");
-        args.push_back(_dbPath.asString().c_str());
 
-        const char* argv[args.size() + opts.size() + 1];
+/****************************************************************/
+/* private member-functions                                    */
+/****************************************************************/
 
-        const char** p = argv;
-        p = copy (args.begin (), args.end (), p);
-        p = copy (opts.begin (), opts.end (), p);
-        *p = 0;
+/*--------------------------------------------------------------*/
+/* Run rpm with the specified arguments, handling stderr       */
+/* as specified  by disp                                       */
+/*--------------------------------------------------------------*/
+void
+RpmDb::run_rpm (const RpmArgVec& opts,
+                ExternalProgram::Stderr_Disposition disp)
+{
+  if ( process )
+  {
+    delete process;
+    process = NULL;
+  }
+  exit_code = -1;
 
-        // Invalidate all outstanding database handles in case
-        // the database gets modified.
-        librpmDb::dbRelease( true );
+  if ( ! initialized() )
+  {
+    ZYPP_THROW(RpmDbNotOpenException());
+  }
 
-        // Launch the program with default locale
-        process = new ExternalProgram(argv, disp, false, -1, true);
-        return;
-      }
+  RpmArgVec args;
 
-      /*--------------------------------------------------------------*/
-      /* Read a line from the rpm process                              */
-      /*--------------------------------------------------------------*/
-      bool
-      RpmDb::systemReadLine(string &line)
-      {
-        line.erase();
+  // always set root and dbpath
+#if defined(WORKAROUNDRPMPWDBUG)
+  args.push_back("#/");                // chdir to / to workaround bnc#819354
+#endif
+  args.push_back("rpm");
+  args.push_back("--root");
+  args.push_back(_root.asString().c_str());
+  args.push_back("--dbpath");
+  args.push_back(_dbPath.asString().c_str());
+
+  const char* argv[args.size() + opts.size() + 1];
+
+  const char** p = argv;
+  p = copy (args.begin (), args.end (), p);
+  p = copy (opts.begin (), opts.end (), p);
+  *p = 0;
+
+  // Invalidate all outstanding database handles in case
+  // the database gets modified.
+  librpmDb::dbRelease( true );
+
+  // Launch the program with default locale
+  process = new ExternalProgram(argv, disp, false, -1, true);
+  return;
+}
+
+/*--------------------------------------------------------------*/
+/* Read a line from the rpm process                            */
+/*--------------------------------------------------------------*/
+bool RpmDb::systemReadLine( std::string & line )
+{
+  line.erase();
 
-        if ( process == NULL )
-          return false;
+  if ( process == NULL )
+    return false;
 
-        line = process->receiveLine();
+  if ( process->inputFile() )
+  {
+    process->setBlocking( false );
+    FILE * inputfile = process->inputFile();
+    int    inputfileFd = ::fileno( inputfile );
+    do
+    {
+      /* Watch inputFile to see when it has input. */
+      fd_set rfds;
+      FD_ZERO( &rfds );
+      FD_SET( inputfileFd, &rfds );
 
-        if (line.length() == 0)
-          return false;
+      /* Wait up to 5 seconds. */
+      struct timeval tv;
+      tv.tv_sec = 5;
+      tv.tv_usec = 0;
 
-        if (line[line.length() - 1] == '\n')
-          line.erase(line.length() - 1);
+      int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
 
-        return true;
+      if ( retval == -1 )
+      {
+       ERR << "select error: " << strerror(errno) << endl;
+       if ( errno != EINTR )
+         return false;
       }
-
-      /*--------------------------------------------------------------*/
-      /* Return the exit status of the rpm process, closing the        */
-      /* connection if not already done                                */
-      /*--------------------------------------------------------------*/
-      int
-      RpmDb::systemStatus()
+      else if ( retval )
       {
-        if ( process == NULL )
-          return -1;
-
-        exit_code = process->close();
-        process->kill();
-        delete process;
-        process = 0;
-
-//   DBG << "exit code " << exit_code << endl;
-
-        return exit_code;
+       // Data is available now.
+       static size_t linebuffer_size = 0;      // static because getline allocs
+       static char * linebuffer = 0;           // and reallocs if buffer is too small
+       ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
+       if ( nread == -1 )
+       {
+         if ( ::feof( inputfile ) )
+           return line.size(); // in case of pending output
+       }
+       else
+       {
+         if ( nread > 0 )
+         {
+           if ( linebuffer[nread-1] == '\n' )
+             --nread;
+           line += std::string( linebuffer, nread );
+         }
+
+         if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
+           return true; // complete line
+       }
+       clearerr( inputfile );
       }
-
-      /*--------------------------------------------------------------*/
-      /* Forcably kill the rpm process                         */
-      /*--------------------------------------------------------------*/
-      void
-      RpmDb::systemKill()
+      else
       {
-        if (process) process->kill();
+       // No data within time.
+       if ( ! process->running() )
+         return false;
       }
+    } while ( true );
+  }
+
+  return false;
+}
+
+/*--------------------------------------------------------------*/
+/* Return the exit status of the rpm process, closing the      */
+/* connection if not already done                              */
+/*--------------------------------------------------------------*/
+int
+RpmDb::systemStatus()
+{
+  if ( process == NULL )
+    return -1;
+
+  exit_code = process->close();
+  if (exit_code == 0)
+    error_message = "";
+  else
+    error_message = process->execError();
+  process->kill();
+  delete process;
+  process = 0;
+
+  //   DBG << "exit code " << exit_code << endl;
+
+  return exit_code;
+}
+
+/*--------------------------------------------------------------*/
+/* Forcably kill the rpm process                               */
+/*--------------------------------------------------------------*/
+void
+RpmDb::systemKill()
+{
+  if (process) process->kill();
+}
 
 
 // generate diff mails for config files
-      void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
-      {
-        string msg = line.substr(9);
-        string::size_type pos1 = string::npos;
-        string::size_type pos2 = string::npos;
-        string file1s, file2s;
-        Pathname file1;
-        Pathname file2;
-
-        pos1 = msg.find (typemsg);
-        for (;;)
-        {
-          if ( pos1 == string::npos )
-            break;
+void RpmDb::processConfigFiles(const std::string& line, const std::string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
+{
+  std::string msg = line.substr(9);
+  std::string::size_type pos1 = std::string::npos;
+  std::string::size_type pos2 = std::string::npos;
+  std::string file1s, file2s;
+  Pathname file1;
+  Pathname file2;
+
+  pos1 = msg.find (typemsg);
+  for (;;)
+  {
+    if ( pos1 == std::string::npos )
+      break;
 
-          pos2 = pos1 + strlen (typemsg);
+    pos2 = pos1 + strlen (typemsg);
 
-          if (pos2 >= msg.length() )
-            break;
+    if (pos2 >= msg.length() )
+      break;
 
-          file1 = msg.substr (0, pos1);
-          file2 = msg.substr (pos2);
+    file1 = msg.substr (0, pos1);
+    file2 = msg.substr (pos2);
 
-          file1s = file1.asString();
-          file2s = file2.asString();
+    file1s = file1.asString();
+    file2s = file2.asString();
 
-          if (!_root.empty() && _root != "/")
-          {
-            file1 = _root + file1;
-            file2 = _root + file2;
-          }
+    if (!_root.empty() && _root != "/")
+    {
+      file1 = _root + file1;
+      file2 = _root + file2;
+    }
 
-          string out;
-          int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
-          if (ret)
+    std::string out;
+    int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
+    if (ret)
+    {
+      Pathname file = _root + WARNINGMAILPATH;
+      if (filesystem::assert_dir(file) != 0)
+      {
+        ERR << "Could not create " << file.asString() << endl;
+        break;
+      }
+      file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
+      std::ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
+      if (!notify)
+      {
+        ERR << "Could not open " <<  file << endl;
+        break;
+      }
+
+      // Translator: %s = name of an rpm package. A list of diffs follows
+      // this message.
+      notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
+      if (ret>1)
+      {
+        ERR << "diff failed" << endl;
+        notify << str::form(difffailmsg,
+                            file1s.c_str(), file2s.c_str()) << endl;
+      }
+      else
+      {
+        notify << str::form(diffgenmsg,
+                            file1s.c_str(), file2s.c_str()) << endl;
+
+        // remove root for the viewer's pleasure (#38240)
+        if (!_root.empty() && _root != "/")
+        {
+          if (out.substr(0,4) == "--- ")
           {
-            Pathname file = _root + WARNINGMAILPATH;
-            if (filesystem::assert_dir(file) != 0)
-            {
-              ERR << "Could not create " << file.asString() << endl;
-              break;
-            }
-            file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
-            ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
-            if (!notify)
-            {
-              ERR << "Could not open " <<  file << endl;
-              break;
-            }
-
-            // Translator: %s = name of an rpm package. A list of diffs follows
-            // this message.
-            notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
-            if (ret>1)
-            {
-              ERR << "diff failed" << endl;
-              notify << str::form(difffailmsg,
-                                  file1s.c_str(), file2s.c_str()) << endl;
-            }
-            else
-            {
-              notify << str::form(diffgenmsg,
-                                  file1s.c_str(), file2s.c_str()) << endl;
-
-              // remove root for the viewer's pleasure (#38240)
-              if (!_root.empty() && _root != "/")
-              {
-                if (out.substr(0,4) == "--- ")
-                {
-                  out.replace(4, file1.asString().length(), file1s);
-                }
-                string::size_type pos = out.find("\n+++ ");
-                if (pos != string::npos)
-                {
-                  out.replace(pos+5, file2.asString().length(), file2s);
-                }
-              }
-              notify << out << endl;
-            }
-            notify.close();
-            notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
-            notify.close();
+            out.replace(4, file1.asString().length(), file1s);
           }
-          else
+          std::string::size_type pos = out.find("\n+++ ");
+          if (pos != std::string::npos)
           {
-            WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
+            out.replace(pos+5, file2.asString().length(), file2s);
           }
-          break;
         }
+        notify << out << endl;
       }
+      notify.close();
+      notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
+      notify.close();
+    }
+    else
+    {
+      WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
+    }
+    break;
+  }
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1834,149 +1971,171 @@ namespace zypp
 //     METHOD NAME : RpmDb::installPackage
 //     METHOD TYPE : PMError
 //
-      void RpmDb::installPackage( const Pathname & filename, unsigned flags )
-      {
-        callback::SendReport<RpmInstallReport> report;
+void RpmDb::installPackage( const Pathname & filename, RpmInstFlags flags )
+{
+  callback::SendReport<RpmInstallReport> report;
 
-        report->start(filename);
+  report->start(filename);
 
-        do
-          try
-          {
-            doInstallPackage(filename, flags, report);
-            report->finish();
-            break;
-          }
-          catch (RpmException & excpt_r)
-          {
-            RpmInstallReport::Action user = report->problem( excpt_r );
-
-            if ( user == RpmInstallReport::ABORT )
-            {
-              report->finish( excpt_r );
-              ZYPP_RETHROW(excpt_r);
-            }
-            else if ( user == RpmInstallReport::IGNORE )
-            {
-              break;
-            }
-          }
-        while (true);
-      }
+  do
+    try
+    {
+      doInstallPackage(filename, flags, report);
+      report->finish();
+      break;
+    }
+    catch (RpmException & excpt_r)
+    {
+      RpmInstallReport::Action user = report->problem( excpt_r );
 
-      void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callback::SendReport<RpmInstallReport> & report )
+      if ( user == RpmInstallReport::ABORT )
       {
-        FAILIFNOTINITIALIZED;
-        CommitLog progresslog;
-
-        MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
-
-
-        // backup
-        if ( _packagebackups )
-        {
-// FIXME      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
-          if ( ! backupPackage( filename ) )
-          {
-            ERR << "backup of " << filename.asString() << " failed" << endl;
-          }
-// FIXME status handling
-          report->progress( 0 ); // allow 1% for backup creation.
-        }
-        else
-        {
-          report->progress( 100 );
-        }
+        report->finish( excpt_r );
+        ZYPP_RETHROW(excpt_r);
+      }
+      else if ( user == RpmInstallReport::IGNORE )
+      {
+        break;
+      }
+    }
+  while (true);
+}
 
-        // run rpm
-        RpmArgVec opts;
-        if (flags & RPMINST_NOUPGRADE)
-          opts.push_back("-i");
-        else
-          opts.push_back("-U");
-        opts.push_back("--percent");
-
-        if (flags & RPMINST_NODIGEST)
-          opts.push_back("--nodigest");
-        if (flags & RPMINST_NOSIGNATURE)
-          opts.push_back("--nosignature");
-        if (flags & RPMINST_NODOCS)
-          opts.push_back ("--excludedocs");
-        if (flags & RPMINST_NOSCRIPTS)
-          opts.push_back ("--noscripts");
-        if (flags & RPMINST_FORCE)
-          opts.push_back ("--force");
-        if (flags & RPMINST_NODEPS)
-          opts.push_back ("--nodeps");
-        if (flags & RPMINST_IGNORESIZE)
-          opts.push_back ("--ignoresize");
-        if (flags & RPMINST_JUSTDB)
-          opts.push_back ("--justdb");
-        if (flags & RPMINST_TEST)
-          opts.push_back ("--test");
-
-        opts.push_back("--");
-        opts.push_back (filename.asString().c_str());
-
-        modifyDatabase(); // BEFORE run_rpm
-        run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
-
-        string line;
-        string rpmmsg;
-        vector<string> configwarnings;
-        vector<string> errorlines;
-
-        while (systemReadLine(line))
-        {
-          if (line.substr(0,2)=="%%")
-          {
-            int percent;
-            sscanf (line.c_str () + 2, "%d", &percent);
-            report->progress( percent );
-          }
-          else
-            rpmmsg += line+'\n';
+void RpmDb::doInstallPackage( const Pathname & filename, RpmInstFlags flags, callback::SendReport<RpmInstallReport> & report )
+{
+  FAILIFNOTINITIALIZED;
+  HistoryLog historylog;
 
-          if ( line.substr(0,8) == "warning:" )
-          {
-            configwarnings.push_back(line);
-          }
-        }
-        int rpm_status = systemStatus();
+  MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
 
-        // evaluate result
-        for (vector<string>::iterator it = configwarnings.begin();
-             it != configwarnings.end(); ++it)
-        {
-          processConfigFiles(*it, Pathname::basename(filename), " saved as ",
-                             // %s = filenames
-                             _("rpm saved %s as %s but it was impossible to determine the difference"),
-                             // %s = filenames
-                             _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
-          processConfigFiles(*it, Pathname::basename(filename), " created as ",
-                             // %s = filenames
-                             _("rpm created %s as %s but it was impossible to determine the difference"),
-                             // %s = filenames
-                             _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
-        }
 
-        if ( rpm_status != 0 )
-        {
-          // %s = filename of rpm package
-          progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
-          progresslog() << _("rpm output:") << endl << rpmmsg << endl;
-          ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
-        }
-        else
-        {
-          // %s = filename of rpm package
-          progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
-          if ( ! rpmmsg.empty() )
-          {
-            progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
-          }
-        }
-      }
+  // backup
+  if ( _packagebackups )
+  {
+    // FIXME      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
+    if ( ! backupPackage( filename ) )
+    {
+      ERR << "backup of " << filename.asString() << " failed" << endl;
+    }
+    // FIXME status handling
+    report->progress( 0 ); // allow 1% for backup creation.
+  }
+
+  // run rpm
+  RpmArgVec opts;
+  if (flags & RPMINST_NOUPGRADE)
+    opts.push_back("-i");
+  else
+    opts.push_back("-U");
+
+  opts.push_back("--percent");
+  opts.push_back("--noglob");
+
+  // ZConfig defines cross-arch installation
+  if ( ! ZConfig::instance().systemArchitecture().compatibleWith( ZConfig::instance().defaultSystemArchitecture() ) )
+    opts.push_back("--ignorearch");
+
+  if (flags & RPMINST_NODIGEST)
+    opts.push_back("--nodigest");
+  if (flags & RPMINST_NOSIGNATURE)
+    opts.push_back("--nosignature");
+  if (flags & RPMINST_EXCLUDEDOCS)
+    opts.push_back ("--excludedocs");
+  if (flags & RPMINST_NOSCRIPTS)
+    opts.push_back ("--noscripts");
+  if (flags & RPMINST_FORCE)
+    opts.push_back ("--force");
+  if (flags & RPMINST_NODEPS)
+    opts.push_back ("--nodeps");
+  if (flags & RPMINST_IGNORESIZE)
+    opts.push_back ("--ignoresize");
+  if (flags & RPMINST_JUSTDB)
+    opts.push_back ("--justdb");
+  if (flags & RPMINST_TEST)
+    opts.push_back ("--test");
+  if (flags & RPMINST_NOPOSTTRANS)
+    opts.push_back ("--noposttrans");
+
+  opts.push_back("--");
+
+  // rpm requires additional quoting of special chars:
+  std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
+  opts.push_back ( quotedFilename.c_str() );
+
+  modifyDatabase(); // BEFORE run_rpm
+  run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+  std::string line;
+  std::string rpmmsg;                          // TODO: immediately forward lines via Callback::report rather than collecting
+  std::vector<std::string> configwarnings;     // TODO: immediately process lines rather than collecting
+
+  unsigned linecnt = 0;
+  while ( systemReadLine( line ) )
+  {
+    if ( str::startsWith( line, "%%" ) )
+    {
+      int percent;
+      sscanf( line.c_str() + 2, "%d", &percent );
+      report->progress( percent );
+      continue;
+    }
+
+    if ( linecnt < MAXRPMMESSAGELINES )
+      ++linecnt;
+    else if ( line.find( " scriptlet failed, " ) == std::string::npos )        // always log %script errors
+      continue;
+
+    rpmmsg += line+'\n';
+
+    if ( str::startsWith( line, "warning:" ) )
+      configwarnings.push_back(line);
+  }
+  if ( linecnt >= MAXRPMMESSAGELINES )
+    rpmmsg += "[truncated]\n";
+
+  int rpm_status = systemStatus();
+
+  // evaluate result
+  for (std::vector<std::string>::iterator it = configwarnings.begin();
+       it != configwarnings.end(); ++it)
+  {
+    processConfigFiles(*it, Pathname::basename(filename), " saved as ",
+                       // %s = filenames
+                       _("rpm saved %s as %s, but it was impossible to determine the difference"),
+                       // %s = filenames
+                       _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
+    processConfigFiles(*it, Pathname::basename(filename), " created as ",
+                       // %s = filenames
+                       _("rpm created %s as %s, but it was impossible to determine the difference"),
+                       // %s = filenames
+                       _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
+  }
+
+  if ( rpm_status != 0 )
+  {
+    historylog.comment(
+        str::form("%s install failed", Pathname::basename(filename).c_str()),
+        true /*timestamp*/);
+    std::ostringstream sstr;
+    sstr << "rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
+    // TranslatorExplanation the colon is followed by an error message
+    ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message : rpmmsg) ));
+  }
+  else if ( ! rpmmsg.empty() )
+  {
+    historylog.comment(
+        str::form("%s installed ok", Pathname::basename(filename).c_str()),
+        true /*timestamp*/);
+    std::ostringstream sstr;
+    sstr << "Additional rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
+
+    // report additional rpm output in finish
+    // TranslatorExplanation Text is followed by a ':'  and the actual output.
+    report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"),  rpmmsg.c_str() ));
+  }
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1984,12 +2143,14 @@ namespace zypp
 //     METHOD NAME : RpmDb::removePackage
 //     METHOD TYPE : PMError
 //
-      void RpmDb::removePackage( Package::constPtr package, unsigned flags )
-      {
-        return removePackage( package->name()
-                              + "-" + package->edition().asString()
-                              + "." + package->arch().asString(), flags );
-      }
+void RpmDb::removePackage( Package::constPtr package, RpmInstFlags flags )
+{
+  // 'rpm -e' does not like epochs
+  return removePackage( package->name()
+                        + "-" + package->edition().version()
+                        + "-" + package->edition().release()
+                        + "." + package->arch().asString(), flags );
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -1997,164 +2158,130 @@ namespace zypp
 //     METHOD NAME : RpmDb::removePackage
 //     METHOD TYPE : PMError
 //
-      void RpmDb::removePackage( const string & name_r, unsigned flags )
-      {
-        callback::SendReport<RpmRemoveReport> report;
+void RpmDb::removePackage( const std::string & name_r, RpmInstFlags flags )
+{
+  callback::SendReport<RpmRemoveReport> report;
 
-        report->start( name_r );
-
-        try
-        {
-          doRemovePackage(name_r, flags, report);
-        }
-        catch (RpmException & excpt_r)
-        {
-          report->finish(excpt_r);
-          ZYPP_RETHROW(excpt_r);
-        }
-        report->finish();
-      }
+  report->start( name_r );
 
+  do
+    try
+    {
+      doRemovePackage(name_r, flags, report);
+      report->finish();
+      break;
+    }
+    catch (RpmException & excpt_r)
+    {
+      RpmRemoveReport::Action user = report->problem( excpt_r );
 
-      void RpmDb::doRemovePackage( const string & name_r, unsigned flags, callback::SendReport<RpmRemoveReport> & report )
+      if ( user == RpmRemoveReport::ABORT )
       {
-        FAILIFNOTINITIALIZED;
-        CommitLog progresslog;
+        report->finish( excpt_r );
+        ZYPP_RETHROW(excpt_r);
+      }
+      else if ( user == RpmRemoveReport::IGNORE )
+      {
+        break;
+      }
+    }
+  while (true);
+}
 
-        MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
 
-        // backup
-        if ( _packagebackups )
-        {
-// FIXME solve this status report somehow
-//      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
-          if ( ! backupPackage( name_r ) )
-          {
-            ERR << "backup of " << name_r << " failed" << endl;
-          }
-          report->progress( 0 );
-        }
-        else
-        {
-          report->progress( 100 );
-        }
+void RpmDb::doRemovePackage( const std::string & name_r, RpmInstFlags flags, callback::SendReport<RpmRemoveReport> & report )
+{
+  FAILIFNOTINITIALIZED;
+  HistoryLog historylog;
 
-        // run rpm
-        RpmArgVec opts;
-        opts.push_back("-e");
-        opts.push_back("--allmatches");
-
-        if (flags & RPMINST_NOSCRIPTS)
-          opts.push_back("--noscripts");
-        if (flags & RPMINST_NODEPS)
-          opts.push_back("--nodeps");
-        if (flags & RPMINST_JUSTDB)
-          opts.push_back("--justdb");
-        if (flags & RPMINST_TEST)
-          opts.push_back ("--test");
-        if (flags & RPMINST_FORCE)
-        {
-          WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
-        }
+  MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
 
-        opts.push_back("--");
-        opts.push_back(name_r.c_str());
+  // backup
+  if ( _packagebackups )
+  {
+    // FIXME solve this status report somehow
+    //      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
+    if ( ! backupPackage( name_r ) )
+    {
+      ERR << "backup of " << name_r << " failed" << endl;
+    }
+    report->progress( 0 );
+  }
+  else
+  {
+    report->progress( 100 );
+  }
+
+  // run rpm
+  RpmArgVec opts;
+  opts.push_back("-e");
+  opts.push_back("--allmatches");
+
+  if (flags & RPMINST_NOSCRIPTS)
+    opts.push_back("--noscripts");
+  if (flags & RPMINST_NODEPS)
+    opts.push_back("--nodeps");
+  if (flags & RPMINST_JUSTDB)
+    opts.push_back("--justdb");
+  if (flags & RPMINST_TEST)
+    opts.push_back ("--test");
+  if (flags & RPMINST_FORCE)
+  {
+    WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
+  }
 
-        modifyDatabase(); // BEFORE run_rpm
-        run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+  opts.push_back("--");
+  opts.push_back(name_r.c_str());
 
-        string line;
-        string rpmmsg;
+  modifyDatabase(); // BEFORE run_rpm
+  run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
 
-        // got no progress from command, so we fake it:
-        // 5  - command started
-        // 50 - command completed
-        // 100 if no error
-        report->progress( 5 );
-        while (systemReadLine(line))
-        {
-          rpmmsg += line+'\n';
-        }
-        report->progress( 50 );
-        int rpm_status = systemStatus();
+  std::string line;
+  std::string rpmmsg;          // TODO: immediately forward lines via Callback::report rather than collecting
 
-        if ( rpm_status != 0 )
-        {
-          // %s = name of rpm package
-          progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
-          progresslog() << _("rpm output:") << endl << rpmmsg << endl;
-          ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
-        }
-        else
-        {
-          progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
-          if ( ! rpmmsg.empty() )
-          {
-            progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
-          }
-        }
-      }
+  // got no progress from command, so we fake it:
+  // 5  - command started
+  // 50 - command completed
+  // 100 if no error
+  report->progress( 5 );
+  unsigned linecnt = 0;
+  while (systemReadLine(line))
+  {
+    if ( linecnt < MAXRPMMESSAGELINES )
+      ++linecnt;
+    else if ( line.find( " scriptlet failed, " ) == std::string::npos )        // always log %script errors
+      continue;
+    rpmmsg += line+'\n';
+  }
+  if ( linecnt >= MAXRPMMESSAGELINES )
+    rpmmsg += "[truncated]\n";
+  report->progress( 50 );
+  int rpm_status = systemStatus();
+
+  if ( rpm_status != 0 )
+  {
+    historylog.comment(
+        str::form("%s remove failed", name_r.c_str()), true /*timestamp*/);
+    std::ostringstream sstr;
+    sstr << "rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
+    // TranslatorExplanation the colon is followed by an error message
+    ZYPP_THROW(RpmSubprocessException(_("RPM failed: ") + (rpmmsg.empty() ? error_message: rpmmsg) ));
+  }
+  else if ( ! rpmmsg.empty() )
+  {
+    historylog.comment(
+        str::form("%s removed ok", name_r.c_str()), true /*timestamp*/);
 
-      string
-      RpmDb::checkPackageResult2string(unsigned code)
-      {
-        string msg;
-        // begin of line characters
-        string bol = " - ";
-        // end of line characters
-        string eol = "\n";
-        if (code == 0)
-          return string(_("Ok"))+eol;
-
-        //translator: these are different kinds of how an rpm package can be broken
-        msg = _("The package is not OK for the following reasons:");
-        msg += eol;
-
-        if (code&CHK_INCORRECT_VERSION)
-        {
-          msg += bol;
-          msg+=_("The package contains different version than expected");
-          msg += eol;
-        }
-        if (code&CHK_INCORRECT_FILEMD5)
-        {
-          msg += bol;
-          msg+=_("The package file has incorrect MD5 sum");
-          msg += eol;
-        }
-        if (code&CHK_GPGSIG_MISSING)
-        {
-          msg += bol;
-          msg+=_("The package is not signed");
-          msg += eol;
-        }
-        if (code&CHK_MD5SUM_MISSING)
-        {
-          msg += bol;
-          msg+=_("The package has no MD5 sum");
-          msg += eol;
-        }
-        if (code&CHK_INCORRECT_GPGSIG)
-        {
-          msg += bol;
-          msg+=_("The package has incorrect signature");
-          msg += eol;
-        }
-        if (code&CHK_INCORRECT_PKGMD5)
-        {
-          msg += bol;
-          msg+=_("The package archive has incorrect MD5 sum");
-          msg += eol;
-        }
-        if (code&CHK_OTHER_FAILURE)
-        {
-          msg += bol;
-          msg+=_("rpm failed for unkown reason, see log file");
-          msg += eol;
-        }
+    std::ostringstream sstr;
+    sstr << "Additional rpm output:" << endl << rpmmsg << endl;
+    historylog.comment(sstr.str());
 
-        return msg;
-      }
+    // report additional rpm output in finish
+    // TranslatorExplanation Text is followed by a ':'  and the actual output.
+    report->finishInfo(str::form( "%s:\n%s\n", _("Additional rpm output"),  rpmmsg.c_str() ));
+  }
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -2162,14 +2289,14 @@ namespace zypp
 //     METHOD NAME : RpmDb::backupPackage
 //     METHOD TYPE : bool
 //
-      bool RpmDb::backupPackage( const Pathname & filename )
-      {
-        RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
-        if ( ! h )
-          return false;
+bool RpmDb::backupPackage( const Pathname & filename )
+{
+  RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
+  if ( ! h )
+    return false;
 
-        return backupPackage( h->tag_name() );
-      }
+  return backupPackage( h->tag_name() );
+}
 
 ///////////////////////////////////////////////////////////////////
 //
@@ -2177,136 +2304,169 @@ namespace zypp
 //     METHOD NAME : RpmDb::backupPackage
 //     METHOD TYPE : bool
 //
-      bool RpmDb::backupPackage(const string& packageName)
-      {
-        CommitLog progresslog;
-        bool ret = true;
-        Pathname backupFilename;
-        Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
+bool RpmDb::backupPackage(const std::string& packageName)
+{
+  HistoryLog progresslog;
+  bool ret = true;
+  Pathname backupFilename;
+  Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
 
-        if (_backuppath.empty())
-        {
-          INT << "_backuppath empty" << endl;
-          return false;
-        }
+  if (_backuppath.empty())
+  {
+    INT << "_backuppath empty" << endl;
+    return false;
+  }
 
-        FileList fileList;
+  FileList fileList;
 
-        if (!queryChangedFiles(fileList, packageName))
-        {
-          ERR << "Error while getting changed files for package " <<
-          packageName << endl;
-          return false;
-        }
+  if (!queryChangedFiles(fileList, packageName))
+  {
+    ERR << "Error while getting changed files for package " <<
+    packageName << endl;
+    return false;
+  }
 
-        if (fileList.size() <= 0)
-        {
-          DBG <<  "package " <<  packageName << " not changed -> no backup" << endl;
-          return true;
-        }
+  if (fileList.size() <= 0)
+  {
+    DBG <<  "package " <<  packageName << " not changed -> no backup" << endl;
+    return true;
+  }
 
-        if (filesystem::assert_dir(_root + _backuppath) != 0)
-        {
-          return false;
-        }
+  if (filesystem::assert_dir(_root + _backuppath) != 0)
+  {
+    return false;
+  }
 
-        {
-          // build up archive name
-          time_t currentTime = time(0);
-          struct tm *currentLocalTime = localtime(&currentTime);
+  {
+    // build up archive name
+    time_t currentTime = time(0);
+    struct tm *currentLocalTime = localtime(&currentTime);
 
-          int date = (currentLocalTime->tm_year + 1900) * 10000
-                     + (currentLocalTime->tm_mon + 1) * 100
-                     + currentLocalTime->tm_mday;
+    int date = (currentLocalTime->tm_year + 1900) * 10000
+               + (currentLocalTime->tm_mon + 1) * 100
+               + currentLocalTime->tm_mday;
 
-          int num = 0;
-          do
-          {
-            backupFilename = _root + _backuppath
-                             + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
+    int num = 0;
+    do
+    {
+      backupFilename = _root + _backuppath
+                       + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
 
-          }
-          while ( PathInfo(backupFilename).isExist() && num++ < 1000);
+    }
+    while ( PathInfo(backupFilename).isExist() && num++ < 1000);
 
-          PathInfo pi(filestobackupfile);
-          if (pi.isExist() && !pi.isFile())
-          {
-            ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
-            return false;
-          }
+    PathInfo pi(filestobackupfile);
+    if (pi.isExist() && !pi.isFile())
+    {
+      ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
+      return false;
+    }
 
-          std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
+    std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
 
-          if (!fp)
-          {
-            ERR << "could not open " << filestobackupfile.asString() << endl;
-            return false;
-          }
+    if (!fp)
+    {
+      ERR << "could not open " << filestobackupfile.asString() << endl;
+      return false;
+    }
 
-          for (FileList::const_iterator cit = fileList.begin();
-               cit != fileList.end(); ++cit)
-          {
-            string name = *cit;
-            if ( name[0] == '/' )
-            {
-              // remove slash, file must be relative to -C parameter of tar
-              name = name.substr( 1 );
-            }
-            DBG << "saving file "<< name << endl;
-            fp << name << endl;
-          }
-          fp.close();
-
-          const char* const argv[] =
-            {
-              "tar",
-              "-czhP",
-              "-C",
-              _root.asString().c_str(),
-              "--ignore-failed-read",
-              "-f",
-              backupFilename.asString().c_str(),
-              "-T",
-              filestobackupfile.asString().c_str(),
-              NULL
-            };
-
-          // execute tar in inst-sys (we dont know if there is a tar below _root !)
-          ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
-
-          string tarmsg;
-
-          // TODO: its probably possible to start tar with -v and watch it adding
-          // files to report progress
-          for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
-          {
-            tarmsg+=output;
-          }
+    for (FileList::const_iterator cit = fileList.begin();
+         cit != fileList.end(); ++cit)
+    {
+      std::string name = *cit;
+      if ( name[0] == '/' )
+      {
+        // remove slash, file must be relative to -C parameter of tar
+        name = name.substr( 1 );
+      }
+      DBG << "saving file "<< name << endl;
+      fp << name << endl;
+    }
+    fp.close();
 
-          int ret = tar.close();
+    const char* const argv[] =
+      {
+        "tar",
+        "-czhP",
+        "-C",
+        _root.asString().c_str(),
+        "--ignore-failed-read",
+        "-f",
+        backupFilename.asString().c_str(),
+        "-T",
+        filestobackupfile.asString().c_str(),
+        NULL
+      };
 
-          if ( ret != 0)
-          {
-            ERR << "tar failed: " << tarmsg << endl;
-            ret = false;
-          }
-          else
-          {
-            MIL << "tar backup ok" << endl;
-            progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
-          }
+    // execute tar in inst-sys (we dont know if there is a tar below _root !)
+    ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
 
-          filesystem::unlink(filestobackupfile);
-        }
+    std::string tarmsg;
 
-        return ret;
-      }
+    // TODO: its probably possible to start tar with -v and watch it adding
+    // files to report progress
+    for (std::string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
+    {
+      tarmsg+=output;
+    }
 
-      void RpmDb::setBackupPath(const Pathname& path)
-      {
-        _backuppath = path;
-      }
+    int ret = tar.close();
+
+    if ( ret != 0)
+    {
+      ERR << "tar failed: " << tarmsg << endl;
+      ret = false;
+    }
+    else
+    {
+      MIL << "tar backup ok" << endl;
+      progresslog.comment(
+          str::form(_("created backup %s"), backupFilename.asString().c_str())
+          , /*timestamp*/true);
+    }
+
+    filesystem::unlink(filestobackupfile);
+  }
+
+  return ret;
+}
+
+void RpmDb::setBackupPath(const Pathname& path)
+{
+  _backuppath = path;
+}
+
+std::ostream & operator<<( std::ostream & str, RpmDb::CheckPackageResult obj )
+{
+  switch ( obj )
+  {
+#define OUTS(E,S) case RpmDb::E: return str << "["<< (unsigned)obj << "-"<< S << "]"; break
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_OK,              _("Signature is OK") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOTFOUND,                _("Unknown type of signature") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_FAIL,            _("Signature does not verify") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOTTRUSTED,      _("Signature is OK, but key is not trusted") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOKEY,           _("Signatures public key is not available") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_ERROR,           _("File does not exist or signature can't be checked") );
+    // translators: possible rpm package signature check result [brief]
+    OUTS( CHK_NOSIG,           _("File is unsigned") );
+#undef OUTS
+  }
+  return str << "UnknowSignatureCheckError("+str::numstring(obj)+")";
+}
+
+std::ostream & operator<<( std::ostream & str, const RpmDb::CheckPackageDetail & obj )
+{
+  for ( const auto & el : obj )
+    str << el.second << endl;
+  return str;
+}
 
-    } // namespace rpm
-  } // namespace target
+} // namespace rpm
+} // namespace target
 } // namespace zypp