RpmDb.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include "librpm.h"
00013 
00014 #include <cstdlib>
00015 #include <cstdio>
00016 #include <ctime>
00017 
00018 #include <iostream>
00019 #include <fstream>
00020 #include <list>
00021 #include <map>
00022 #include <set>
00023 #include <string>
00024 #include <vector>
00025 #include <algorithm>
00026 
00027 #include "zypp/base/Logger.h"
00028 #include "zypp/base/String.h"
00029 
00030 #include "zypp/Date.h"
00031 #include "zypp/Pathname.h"
00032 #include "zypp/PathInfo.h"
00033 #include "zypp/PublicKey.h"
00034 
00035 #include "zypp/target/rpm/RpmDb.h"
00036 #include "zypp/target/rpm/RpmCallbacks.h"
00037 
00038 #include "zypp/target/CommitLog.h"
00039 #include "zypp/target/rpm/librpmDb.h"
00040 #include "zypp/target/rpm/RpmPackageImpl.h"
00041 #include "zypp/target/rpm/RpmException.h"
00042 #include "zypp/CapSet.h"
00043 #include "zypp/CapFactory.h"
00044 #include "zypp/KeyRing.h"
00045 #include "zypp/ZYppFactory.h"
00046 #include "zypp/TmpPath.h"
00047 
00048 #ifndef _
00049 #define _(X) X
00050 #endif
00051 
00052 using namespace std;
00053 using namespace zypp::filesystem;
00054 
00055 namespace zypp
00056 {
00057   namespace target
00058   {
00059     namespace rpm
00060     {
00061       namespace
00062       {
00063         const char* quoteInFilename_m = " \t";
00064         inline std::string rpmQuoteFilename( const Pathname & path_r )
00065         {
00066           std::string path( path_r.asString() );
00067           for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
00068                 pos != std::string::npos;
00069                 pos = path.find_first_of( quoteInFilename_m, pos ) )
00070             {
00071               path.insert( pos, "\\" );
00072               pos += 2; // skip '\\' and the quoted char.
00073             }
00074           return path;
00075         }
00076       }
00077 
00078       struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
00079       {
00080         KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
00081         {
00082           connect();
00083         }
00084 
00085         ~KeyRingSignalReceiver()
00086         {
00087           disconnect();
00088         }
00089 
00090         virtual void trustedKeyAdded( const KeyRing &keyring, const PublicKey &key )
00091         {
00092           MIL << "trusted key added to zypp Keyring. Syncronizing keys with rpm keyring" << std::endl;
00093           _rpmdb.importZyppKeyRingTrustedKeys();
00094           _rpmdb.exportTrustedKeysInZyppKeyRing();
00095         }
00096 
00097         virtual void trustedKeyRemoved( const KeyRing &keyring, const PublicKey &key  )
00098         {
00099         }
00100 
00101         RpmDb &_rpmdb;
00102       };
00103 
00104       static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
00105 
00106       unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
00107       {
00108         const char* argv[] =
00109         {
00110           "diff",
00111           "-u",
00112           file1.c_str(),
00113           file2.c_str(),
00114           NULL
00115         };
00116         ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00117 
00118         //if(!prog)
00119         //return 2;
00120 
00121         string line;
00122         int count = 0;
00123         for (line = prog.receiveLine(), count=0;
00124               !line.empty();
00125               line = prog.receiveLine(), count++ )
00126           {
00127             if (maxlines<0?true:count<maxlines)
00128               out+=line;
00129           }
00130 
00131         return prog.close();
00132       }
00133 
00134 
00135 
00136       /******************************************************************
00137        **
00138        **
00139        **       FUNCTION NAME : stringPath
00140        **       FUNCTION TYPE : inline string
00141       */
00142       inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
00143       {
00144         return librpmDb::stringPath( root_r, sub_r );
00145       }
00146 
00147       /******************************************************************
00148        **
00149        **
00150        **       FUNCTION NAME : operator<<
00151        **       FUNCTION TYPE : ostream &
00152       */
00153       ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
00154       {
00155         if ( obj == RpmDb::DbSI_NO_INIT )
00156           {
00157             str << "NO_INIT";
00158           }
00159         else
00160           {
00161 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
00162             str << "V4(";
00163             ENUM_OUT( DbSI_HAVE_V4,     'X' );
00164             ENUM_OUT( DbSI_MADE_V4,     'c' );
00165             ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
00166             str << ")V3(";
00167             ENUM_OUT( DbSI_HAVE_V3,     'X' );
00168             ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
00169             ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
00170             str << ")";
00171 #undef ENUM_OUT
00172           }
00173         return str;
00174       }
00175 
00177       //        CLASS NAME : RpmDbPtr
00178       //        CLASS NAME : RpmDbconstPtr
00180 
00181 #define WARNINGMAILPATH "/var/log/YaST2/"
00182 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
00183 
00185       //
00186       //        CLASS NAME : RpmDb::Packages
00205       class RpmDb::Packages
00206       {
00207       public:
00208         list<Package::Ptr>        _list;
00209         map<std::string,Package::Ptr> _index;
00210         bool                      _valid;
00211         Packages() : _valid( false )
00212         {}
00213         void clear()
00214         {
00215           _list.clear();
00216           _index.clear();
00217           _valid = false;
00218         }
00219         Package::Ptr lookup( const string & name_r ) const
00220         {
00221           map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
00222           if ( got != _index.end() )
00223             return got->second;
00224           return Package::Ptr();
00225         }
00226         void buildIndex()
00227         {
00228           _index.clear();
00229           for ( list<Package::Ptr>::iterator iter = _list.begin();
00230                 iter != _list.end(); ++iter )
00231             {
00232               string name = (*iter)->name();
00233               Package::Ptr & nptr = _index[name]; // be shure to get a reference!
00234 
00235               if ( nptr )
00236                 {
00237                   WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
00238                   if ( nptr->installtime() > (*iter)->installtime() )
00239                     continue;
00240                   else
00241                     nptr = *iter;
00242                 }
00243               else
00244                 {
00245                   nptr = *iter;
00246                 }
00247             }
00248           _valid = true;
00249         }
00250       };
00251 
00253 
00255       //
00256       //        CLASS NAME : RpmDb
00257       //
00259 
00260 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
00261 
00263 
00265       //
00266       //
00267       //        METHOD NAME : RpmDb::RpmDb
00268       //        METHOD TYPE : Constructor
00269       //
00270       RpmDb::RpmDb()
00271       : _dbStateInfo( DbSI_NO_INIT )
00272       , _packages( * new Packages ) // delete in destructor
00273 #warning Check for obsolete memebers
00274       , _backuppath ("/var/adm/backup")
00275       , _packagebackups(false)
00276       , _warndirexists(false)
00277       {
00278         process = 0;
00279         exit_code = -1;
00280 
00281         // Some rpm versions are patched not to abort installation if
00282         // symlink creation failed.
00283         setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
00284         sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
00285       }
00286 
00288       //
00289       //
00290       //        METHOD NAME : RpmDb::~RpmDb
00291       //        METHOD TYPE : Destructor
00292       //
00293       RpmDb::~RpmDb()
00294       {
00295         MIL << "~RpmDb()" << endl;
00296         closeDatabase();
00297 
00298         delete process;
00299         delete &_packages;
00300         MIL  << "~RpmDb() end" << endl;
00301         sKeyRingReceiver.reset();
00302       }
00303 
00304       Date RpmDb::timestamp() const
00305       {
00306         Date ts_rpm;
00307 
00308         Pathname db_path;
00309         if ( dbPath().empty() )
00310           db_path = "/var/lib/rpm";
00311         else
00312           db_path = dbPath();
00313 
00314         PathInfo rpmdb_info(root() + db_path + "/Packages");
00315 
00316         if ( rpmdb_info.isExist() )
00317           return rpmdb_info.mtime();
00318         else
00319           return Date::now();
00320       }
00322       //
00323       //
00324       //        METHOD NAME : RpmDb::dumpOn
00325       //        METHOD TYPE : std::ostream &
00326       //
00327       std::ostream & RpmDb::dumpOn( std::ostream & str ) const
00328       {
00329         str << "RpmDb[";
00330 
00331         if ( _dbStateInfo == DbSI_NO_INIT )
00332           {
00333             str << "NO_INIT";
00334           }
00335         else
00336           {
00337 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
00338             str << "V4(";
00339             ENUM_OUT( DbSI_HAVE_V4,     'X' );
00340             ENUM_OUT( DbSI_MADE_V4,     'c' );
00341             ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
00342             str << ")V3(";
00343             ENUM_OUT( DbSI_HAVE_V3,     'X' );
00344             ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
00345             ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
00346             str << "): " << stringPath( _root, _dbPath );
00347 #undef ENUM_OUT
00348           }
00349         return str << "]";
00350       }
00351 
00353       //
00354       //
00355       //        METHOD NAME : RpmDb::initDatabase
00356       //        METHOD TYPE : PMError
00357       //
00358       void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
00359       {
00361         // Check arguments
00363         if ( root_r.empty() )
00364           root_r = "/";
00365 
00366         if ( dbPath_r.empty() )
00367           dbPath_r = "/var/lib/rpm";
00368 
00369         if ( ! (root_r.absolute() && dbPath_r.absolute()) )
00370           {
00371             ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
00372             ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
00373           }
00374 
00375         MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
00376 
00378         // Check whether already initialized
00380         if ( initialized() )
00381           {
00382             if ( root_r == _root && dbPath_r == _dbPath )
00383               {
00384                 return;
00385               }
00386             else
00387               {
00388                 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
00389               }
00390           }
00391 
00393         // init database
00395         librpmDb::unblockAccess();
00396         DbStateInfoBits info = DbSI_NO_INIT;
00397         try
00398           {
00399             internal_initDatabase( root_r, dbPath_r, info );
00400           }
00401         catch (const RpmException & excpt_r)
00402           {
00403             ZYPP_CAUGHT(excpt_r);
00404             librpmDb::blockAccess();
00405             ERR << "Cleanup on error: state " << info << endl;
00406 
00407             if ( dbsi_has( info, DbSI_MADE_V4 ) )
00408               {
00409                 // remove the newly created rpm4 database and
00410                 // any backup created on conversion.
00411                 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
00412               }
00413             ZYPP_RETHROW(excpt_r);
00414           }
00415         if ( dbsi_has( info, DbSI_HAVE_V3 ) )
00416           {
00417             if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
00418               {
00419                 // Move obsolete rpm3 database beside.
00420                 MIL << "Cleanup: state " << info << endl;
00421                 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
00422                 dbsi_clr( info, DbSI_HAVE_V3 );
00423               }
00424             else
00425               {
00426                 // Performing an update: Keep the original rpm3 database
00427                 // and wait if the rpm4 database gets modified by installing
00428                 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
00429                 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
00430               }
00431           }
00432 #warning CHECK: notify root about conversion backup.
00433 
00434         _root   = root_r;
00435         _dbPath = dbPath_r;
00436         _dbStateInfo = info;
00437 
00438 #warning Add rebuild database once have the info about context
00439 #if 0
00440         if ( ! ( Y2PM::runningFromSystem() ) )
00441           {
00442             if (      dbsi_has( info, DbSI_HAVE_V4 )
00443                  && ! dbsi_has( info, DbSI_MADE_V4 ) )
00444               {
00445                 err = rebuildDatabase();
00446               }
00447           }
00448 #endif
00449 
00450         MIL << "Syncronizing keys with zypp keyring" << std::endl;
00451         importZyppKeyRingTrustedKeys();
00452         exportTrustedKeysInZyppKeyRing();
00453 
00454         // Close the database in case any write acces (create/convert)
00455         // happened during init. This should drop any lock acquired
00456         // by librpm. On demand it will be reopened readonly and should
00457         // not hold any lock.
00458         librpmDb::dbRelease( true );
00459 
00460         MIL << "InitDatabase: " << *this << endl;
00461       }
00462 
00464       //
00465       //
00466       //        METHOD NAME : RpmDb::internal_initDatabase
00467       //        METHOD TYPE : PMError
00468       //
00469       void RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
00470                                          DbStateInfoBits & info_r )
00471       {
00472         info_r = DbSI_NO_INIT;
00473 
00475         // Get info about the desired database dir
00477         librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
00478 
00479         if ( dbInfo.illegalArgs() )
00480           {
00481             // should not happen (checked in initDatabase)
00482             ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
00483           }
00484         if ( ! dbInfo.usableArgs() )
00485           {
00486             ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
00487             ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
00488           }
00489 
00490         if ( dbInfo.hasDbV4() )
00491           {
00492             dbsi_set( info_r, DbSI_HAVE_V4 );
00493             MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
00494           }
00495         else
00496           {
00497             MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
00498           }
00499 
00500         if ( dbInfo.hasDbV3() )
00501           {
00502             dbsi_set( info_r, DbSI_HAVE_V3 );
00503           }
00504         if ( dbInfo.hasDbV3ToV4() )
00505           {
00506             dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
00507           }
00508 
00509         DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
00510         librpmDb::dumpState( DBG ) << endl;
00511 
00513         // Access database, create if needed
00515 
00516         // creates dbdir and empty rpm4 database if not present
00517         librpmDb::dbAccess( root_r, dbPath_r );
00518 
00519         if ( ! dbInfo.hasDbV4() )
00520           {
00521             dbInfo.restat();
00522             if ( dbInfo.hasDbV4() )
00523               {
00524                 dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
00525               }
00526           }
00527 
00528         DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
00529         librpmDb::dumpState( DBG ) << endl;
00530 
00532         // Check whether to convert something. Create backup but do
00533         // not remove anything here
00535         librpmDb::constPtr dbptr;
00536         librpmDb::dbAccess( dbptr );
00537         bool dbEmpty = dbptr->empty();
00538         if ( dbEmpty )
00539           {
00540             MIL << "Empty rpm4 database "  << dbInfo.dbV4() << endl;
00541           }
00542 
00543         if ( dbInfo.hasDbV3() )
00544           {
00545             MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
00546 
00547             if ( dbEmpty )
00548               {
00549                 extern void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
00550                 convertV3toV4( dbInfo.dbV3().path(), dbptr );
00551 
00552                 // create a backup copy
00553                 int res = filesystem::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
00554                 if ( res )
00555                   {
00556                     WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
00557                   }
00558                 else
00559                   {
00560                     dbInfo.restat();
00561                     if ( dbInfo.hasDbV3ToV4() )
00562                       {
00563                         MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
00564                         dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
00565                       }
00566                   }
00567 
00568               }
00569             else
00570               {
00571 
00572                 WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
00573 #warning EXCEPTION: nonempty rpm4 and rpm3 database found.
00574                 //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
00575                 //report->start( dbInfo.dbV3().path() );
00576                 //report->stop( some error );
00577 
00578                 // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
00579                 dbsi_set( info_r, DbSI_MODIFIED_V4 );
00580 
00581               }
00582 
00583             DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
00584             librpmDb::dumpState( DBG ) << endl;
00585           }
00586 
00587         if ( dbInfo.hasDbV3ToV4() )
00588           {
00589             MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
00590           }
00591       }
00592 
00594       //
00595       //
00596       //        METHOD NAME : RpmDb::removeV4
00597       //        METHOD TYPE : void
00598       //
00599       void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
00600       {
00601         const char * v3backup = "packages.rpm3";
00602         const char * master = "Packages";
00603         const char * index[] =
00604         {
00605           "Basenames",
00606           "Conflictname",
00607           "Depends",
00608           "Dirnames",
00609           "Filemd5s",
00610           "Group",
00611           "Installtid",
00612           "Name",
00613           "Providename",
00614           "Provideversion",
00615           "Pubkeys",
00616           "Requirename",
00617           "Requireversion",
00618           "Sha1header",
00619           "Sigmd5",
00620           "Triggername",
00621           // last entry!
00622           NULL
00623         };
00624 
00625         PathInfo pi( dbdir_r );
00626         if ( ! pi.isDir() )
00627           {
00628             ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
00629             return;
00630           }
00631 
00632         for ( const char ** f = index; *f; ++f )
00633           {
00634             pi( dbdir_r + *f );
00635             if ( pi.isFile() )
00636               {
00637                 filesystem::unlink( pi.path() );
00638               }
00639           }
00640 
00641         pi( dbdir_r + master );
00642         if ( pi.isFile() )
00643           {
00644             MIL << "Removing rpm4 database " << pi << endl;
00645             filesystem::unlink( pi.path() );
00646           }
00647 
00648         if ( v3backup_r )
00649           {
00650             pi( dbdir_r + v3backup );
00651             if ( pi.isFile() )
00652               {
00653                 MIL << "Removing converted rpm3 database backup " << pi << endl;
00654                 filesystem::unlink( pi.path() );
00655               }
00656           }
00657       }
00658 
00660       //
00661       //
00662       //        METHOD NAME : RpmDb::removeV3
00663       //        METHOD TYPE : void
00664       //
00665       void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
00666       {
00667         const char * master = "packages.rpm";
00668         const char * index[] =
00669         {
00670           "conflictsindex.rpm",
00671           "fileindex.rpm",
00672           "groupindex.rpm",
00673           "nameindex.rpm",
00674           "providesindex.rpm",
00675           "requiredby.rpm",
00676           "triggerindex.rpm",
00677           // last entry!
00678           NULL
00679         };
00680 
00681         PathInfo pi( dbdir_r );
00682         if ( ! pi.isDir() )
00683           {
00684             ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
00685             return;
00686           }
00687 
00688         for ( const char ** f = index; *f; ++f )
00689           {
00690             pi( dbdir_r + *f );
00691             if ( pi.isFile() )
00692               {
00693                 filesystem::unlink( pi.path() );
00694               }
00695           }
00696 
00697 #warning CHECK: compare vs existing v3 backup. notify root
00698         pi( dbdir_r + master );
00699         if ( pi.isFile() )
00700           {
00701             Pathname m( pi.path() );
00702             if ( v3backup_r )
00703               {
00704                 // backup was already created
00705                 filesystem::unlink( m );
00706                 Pathname b( m.extend( "3" ) );
00707                 pi( b ); // stat backup
00708               }
00709             else
00710               {
00711                 Pathname b( m.extend( ".deleted" ) );
00712                 pi( b );
00713                 if ( pi.isFile() )
00714                   {
00715                     // rempve existing backup
00716                     filesystem::unlink( b );
00717                   }
00718                 filesystem::rename( m, b );
00719                 pi( b ); // stat backup
00720               }
00721             MIL << "(Re)moved rpm3 database to " << pi << endl;
00722           }
00723       }
00724 
00726       //
00727       //
00728       //        METHOD NAME : RpmDb::modifyDatabase
00729       //        METHOD TYPE : void
00730       //
00731       void RpmDb::modifyDatabase()
00732       {
00733         if ( ! initialized() )
00734           return;
00735 
00736         // tag database as modified
00737         dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
00738 
00739         // Move outdated rpm3 database beside.
00740         if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
00741           {
00742             MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
00743             removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
00744             dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
00745           }
00746 
00747         // invalidate Packages list
00748         _packages._valid = false;
00749       }
00750 
00752       //
00753       //
00754       //        METHOD NAME : RpmDb::closeDatabase
00755       //        METHOD TYPE : PMError
00756       //
00757       void RpmDb::closeDatabase()
00758       {
00759         if ( ! initialized() )
00760           {
00761             return;
00762           }
00763 
00764         MIL << "Calling closeDatabase: " << *this << endl;
00765 
00767         // Block further database access
00769         _packages.clear();
00770         librpmDb::blockAccess();
00771 
00773         // Check fate if old version database still present
00775         if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) )
00776           {
00777             MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
00778             if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) )
00779               {
00780                 // Move outdated rpm3 database beside.
00781                 removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 )  );
00782               }
00783             else
00784               {
00785                 // Remove unmodified rpm4 database
00786                 removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
00787               }
00788           }
00789 
00791         // Uninit
00793         _root = _dbPath = Pathname();
00794         _dbStateInfo = DbSI_NO_INIT;
00795 
00796         MIL << "closeDatabase: " << *this << endl;
00797       }
00798 
00800       //
00801       //
00802       //        METHOD NAME : RpmDb::rebuildDatabase
00803       //        METHOD TYPE : PMError
00804       //
00805       void RpmDb::rebuildDatabase()
00806       {
00807         callback::SendReport<RebuildDBReport> report;
00808 
00809         report->start( root() + dbPath() );
00810 
00811         try
00812           {
00813             doRebuildDatabase(report);
00814           }
00815         catch (RpmException & excpt_r)
00816           {
00817             report->finish(root() + dbPath(), RebuildDBReport::FAILED, excpt_r.asUserString());
00818             ZYPP_RETHROW(excpt_r);
00819           }
00820         report->finish(root() + dbPath(), RebuildDBReport::NO_ERROR, "");
00821       }
00822 
00823       void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
00824       {
00825         FAILIFNOTINITIALIZED;
00826 
00827         MIL << "RpmDb::rebuildDatabase" << *this << endl;
00828         // FIXME  Timecount _t( "RpmDb::rebuildDatabase" );
00829 
00830         PathInfo dbMaster( root() + dbPath() + "Packages" );
00831         PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
00832 
00833         // run rpm
00834         RpmArgVec opts;
00835         opts.push_back("--rebuilddb");
00836         opts.push_back("-vv");
00837 
00838         // don't call modifyDatabase because it would remove the old
00839         // rpm3 database, if the current database is a temporary one.
00840         // But do invalidate packages list.
00841         _packages._valid = false;
00842         run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
00843 
00844         // progress report: watch this file growing
00845         PathInfo newMaster( root()
00846                             + dbPath().extend( str::form( "rebuilddb.%d",
00847                                                           process?process->getpid():0) )
00848                             + "Packages" );
00849 
00850         string       line;
00851         string       errmsg;
00852 
00853         while ( systemReadLine( line ) )
00854           {
00855             if ( newMaster() )
00856               { // file is removed at the end of rebuild.
00857                 // current size should be upper limit for new db
00858                 report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath());
00859               }
00860 
00861             if ( line.compare( 0, 2, "D:" ) )
00862               {
00863                 errmsg += line + '\n';
00864                 //      report.notify( line );
00865                 WAR << line << endl;
00866               }
00867           }
00868 
00869         int rpm_status = systemStatus();
00870 
00871         if ( rpm_status != 0 )
00872           {
00873             ZYPP_THROW(RpmSubprocessException(string("rpm failed with message: ") + errmsg));
00874           }
00875         else
00876           {
00877             report->progress( 100, root() + dbPath() ); // 100%
00878           }
00879       }
00880 
00881       void RpmDb::exportTrustedKeysInZyppKeyRing()
00882       {
00883         MIL << "Exporting rpm keyring into zypp trusted keyring" <<std::endl;
00884 
00885         std::set<Edition> rpm_keys = pubkeyEditions();
00886 
00887         std::list<PublicKey> zypp_keys;
00888         zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
00889 
00890         for ( std::set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
00891           {
00892             // search the zypp key into the rpm keys
00893             // long id is edition version + release
00894             std::string id = str::toUpper( (*it).version() + (*it).release());
00895             std::list<PublicKey>::iterator ik = find( zypp_keys.begin(), zypp_keys.end(), id);
00896             if ( ik != zypp_keys.end() )
00897               {
00898                 MIL << "Key " << (*it) << " is already in zypp database." << std::endl;
00899               }
00900             else
00901               {
00902                 // we export the rpm key into a file
00903                 RpmHeader::constPtr result = new RpmHeader();
00904                 getData( std::string("gpg-pubkey"), *it, result );
00905                 TmpFile file(getZYpp()->tmpPath());
00906                 std::ofstream os;
00907                 try
00908                   {
00909                     os.open(file.path().asString().c_str());
00910                     // dump rpm key into the tmp file
00911                     os << result->tag_description();
00912                     //MIL << "-----------------------------------------------" << std::endl;
00913                     //MIL << result->tag_description() <<std::endl;
00914                     //MIL << "-----------------------------------------------" << std::endl;
00915                     os.close();
00916                   }
00917                 catch (std::exception &e)
00918                   {
00919                     ERR << "Could not dump key " << (*it) << " in tmp file " << file.path() << std::endl;
00920                     // just ignore the key
00921                   }
00922 
00923                 // now import the key in zypp
00924                 try
00925                   {
00926                     getZYpp()->keyRing()->importKey( file.path(), true /*trusted*/);
00927                     MIL << "Trusted key " << (*it) << " imported in zypp keyring." << std::endl;
00928                   }
00929                 catch (Exception &e)
00930                   {
00931                     ERR << "Could not import key " << (*it) << " in zypp keyring" << std::endl;
00932                   }
00933               }
00934           }
00935       }
00936 
00937       void RpmDb::importZyppKeyRingTrustedKeys()
00938       {
00939         MIL << "Importing zypp trusted keyring" << std::endl;
00940 
00941         std::list<PublicKey> rpm_keys = pubkeys();
00942 
00943         std::list<PublicKey> zypp_keys;
00944 
00945         zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
00946 
00947         for ( std::list<PublicKey>::const_iterator it = zypp_keys.begin(); it != zypp_keys.end(); ++it)
00948           {
00949             // we find only the left part of the long gpg key, as rpm does not support long ids
00950             std::list<PublicKey>::iterator ik = find( rpm_keys.begin(), rpm_keys.end(), (*it));
00951             if ( ik != rpm_keys.end() )
00952               {
00953                 MIL << "Key " << (*it).id() << " (" << (*it).name() << ") is already in rpm database." << std::endl;
00954               }
00955             else
00956               {
00957                 // now import the key in rpm
00958                 try
00959                   {
00960                     importPubkey((*it).path());
00961                     MIL << "Trusted key " << (*it).id() << " (" << (*it).name() << ") imported in rpm database." << std::endl;
00962                   }
00963                 catch (RpmException &e)
00964                   {
00965                     ERR << "Could not import key " << (*it).id() << " (" << (*it).name() << " from " << (*it).path() << " in rpm database" << std::endl;
00966                   }
00967               }
00968           }
00969       }
00970 
00972       //
00973       //
00974       //        METHOD NAME : RpmDb::importPubkey
00975       //        METHOD TYPE : PMError
00976       //
00977       void RpmDb::importPubkey( const Pathname & pubkey_r )
00978       {
00979         FAILIFNOTINITIALIZED;
00980 
00981         RpmArgVec opts;
00982         opts.push_back ( "--import" );
00983         opts.push_back ( "--" );
00984         opts.push_back ( pubkey_r.asString().c_str() );
00985 
00986         // don't call modifyDatabase because it would remove the old
00987         // rpm3 database, if the current database is a temporary one.
00988         // But do invalidate packages list.
00989         _packages._valid = false;
00990         run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
00991 
00992         string line;
00993         while ( systemReadLine( line ) )
00994           {
00995             if ( line.substr( 0, 6 ) == "error:" )
00996               {
00997                 WAR << line << endl;
00998               }
00999             else
01000               {
01001                 DBG << line << endl;
01002               }
01003           }
01004 
01005         int rpm_status = systemStatus();
01006 
01007         if ( rpm_status != 0 )
01008           {
01009             ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.asString() + string(": rpm returned  ") + str::numstring(rpm_status)));
01010           }
01011         else
01012           {
01013             MIL << "Imported public key from file " << pubkey_r << endl;
01014           }
01015       }
01016 
01018       //
01019       //
01020       //        METHOD NAME : RpmDb::pubkeys
01021       //        METHOD TYPE : set<Edition>
01022       //
01023       list<PublicKey> RpmDb::pubkeys() const
01024       {
01025         list<PublicKey> ret;
01026 
01027         librpmDb::db_const_iterator it;
01028         for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
01029           {
01030             Edition edition = it->tag_edition();
01031             if (edition != Edition::noedition)
01032               {
01033                 // we export the rpm key into a file
01034                 RpmHeader::constPtr result = new RpmHeader();
01035                 getData( std::string("gpg-pubkey"), edition, result );
01036                 TmpFile file(getZYpp()->tmpPath());
01037                 std::ofstream os;
01038                 try
01039                   {
01040                     os.open(file.path().asString().c_str());
01041                     // dump rpm key into the tmp file
01042                     os << result->tag_description();
01043                     //MIL << "-----------------------------------------------" << std::endl;
01044                     //MIL << result->tag_description() <<std::endl;
01045                     //MIL << "-----------------------------------------------" << std::endl;
01046                     os.close();
01047                     // read the public key from the dumped file
01048                     PublicKey key(file.path());
01049                     ret.push_back(key);
01050                   }
01051                 catch (std::exception &e)
01052                   {
01053                     ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << std::endl;
01054                     // just ignore the key
01055                   }
01056               }
01057           }
01058         return ret;
01059       }
01060 
01061       set<Edition> RpmDb::pubkeyEditions() const
01062       {
01063         set<Edition> ret;
01064 
01065         librpmDb::db_const_iterator it;
01066         for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
01067           {
01068             Edition edition = it->tag_edition();
01069             if (edition != Edition::noedition)
01070               ret.insert( edition );
01071           }
01072         return ret;
01073       }
01074 
01076       //
01077       //
01078       //        METHOD NAME : RpmDb::packagesValid
01079       //        METHOD TYPE : bool
01080       //
01081       bool RpmDb::packagesValid() const
01082       {
01083         return( _packages._valid || ! initialized() );
01084       }
01085 
01087       //
01088       //
01089       //        METHOD NAME : RpmDb::getPackages
01090       //        METHOD TYPE : const std::list<Package::Ptr> &
01091       //
01092       //        DESCRIPTION :
01093       //
01094       const std::list<Package::Ptr> & RpmDb::getPackages()
01095       {
01096         callback::SendReport<ScanDBReport> report;
01097 
01098         report->start ();
01099 
01100         try
01101           {
01102             const std::list<Package::Ptr> & ret = doGetPackages(report);
01103             report->finish(ScanDBReport::NO_ERROR, "");
01104             return ret;
01105           }
01106         catch (RpmException & excpt_r)
01107           {
01108             report->finish(ScanDBReport::FAILED, excpt_r.asUserString ());
01109             ZYPP_RETHROW(excpt_r);
01110           }
01111 #warning fixme
01112         static const std::list<Package::Ptr> empty_list;
01113         return empty_list;
01114       }
01115 
01116 
01117       //
01118       // make Package::Ptr from RpmHeader
01119       // return NULL on error
01120       //
01121       Package::Ptr RpmDb::makePackageFromHeader( const RpmHeader::constPtr header, std::set<std::string> * filerequires, const Pathname & location, Source_Ref source )
01122       {
01123         if ( ! header )
01124           return 0;
01125 
01126         if ( header->isSrc() )
01127           {
01128             WAR << "Can't make Package from SourcePackage header" << endl;
01129             return 0;
01130           }
01131 
01132         Package::Ptr pptr;
01133 
01134         string name = header->tag_name();
01135 
01136         // create dataprovider
01137         detail::ResImplTraits<RPMPackageImpl>::Ptr impl( new RPMPackageImpl( header ) );
01138 
01139         impl->setSource( source );
01140         if (!location.empty())
01141           impl->setLocation( location );
01142 
01143         Edition edition;
01144         try
01145           {
01146             edition = Edition( header->tag_version(),
01147                                header->tag_release(),
01148                                header->tag_epoch());
01149           }
01150         catch (Exception & excpt_r)
01151           {
01152             ZYPP_CAUGHT( excpt_r );
01153             WAR << "Package " << name << " has bad edition '"
01154             << (header->tag_epoch().empty()?"":(header->tag_epoch()+":"))
01155             << header->tag_version()
01156             << (header->tag_release().empty()?"":(string("-") + header->tag_release())) << "'";
01157             return pptr;
01158           }
01159 
01160         Arch arch;
01161         try
01162           {
01163             arch = Arch( header->tag_arch() );
01164           }
01165         catch (Exception & excpt_r)
01166           {
01167             ZYPP_CAUGHT( excpt_r );
01168             WAR << "Package " << name << " has bad architecture '" << header->tag_arch() << "'";
01169             return pptr;
01170           }
01171 
01172         // Collect basic Resolvable data
01173         NVRAD dataCollect( header->tag_name(),
01174                            edition,
01175                            arch );
01176 
01177         list<string> filenames = impl->filenames();
01178         dataCollect[Dep::PROVIDES] = header->tag_provides ( filerequires );
01179         CapFactory capfactory;
01180 
01181         static str::smatch what;
01182         static const str::regex filenameRegex( "/(s?bin|lib(64)?|etc)/|^/usr/(games/|share/(dict/words|magic\\.mime)$)|^/opt/gnome/games/",
01183                                                str::regex::optimize|str::regex::nosubs );
01184 
01185         for (list<string>::const_iterator filename = filenames.begin();
01186               filename != filenames.end();
01187               ++filename)
01188           {
01189             if ( str::regex_search( filename->begin(), filename->end(), what, filenameRegex ) )
01190               {
01191                 try
01192                   {
01193                     dataCollect[Dep::PROVIDES].insert( capfactory.parse(ResTraits<Package>::kind, *filename) );
01194                   }
01195                 catch (Exception & excpt_r)
01196                   {
01197                     ZYPP_CAUGHT( excpt_r );
01198                     WAR << "Ignoring invalid capability: " << *filename << endl;
01199                   }
01200               }
01201           }
01202 
01203         dataCollect[Dep::REQUIRES]    = header->tag_requires( filerequires );
01204         dataCollect[Dep::PREREQUIRES] = header->tag_prerequires( filerequires );
01205         dataCollect[Dep::CONFLICTS]   = header->tag_conflicts( filerequires );
01206         dataCollect[Dep::OBSOLETES]   = header->tag_obsoletes( filerequires );
01207         dataCollect[Dep::ENHANCES]    = header->tag_enhances( filerequires );
01208         dataCollect[Dep::SUPPLEMENTS] = header->tag_supplements( filerequires );
01209 
01210         try
01211           {
01212             // create package from dataprovider
01213             pptr = detail::makeResolvableFromImpl( dataCollect, impl );
01214           }
01215         catch (Exception & excpt_r)
01216           {
01217             ZYPP_CAUGHT( excpt_r );
01218             ERR << "Can't create Package::Ptr" << endl;
01219           }
01220 
01221         return pptr;
01222       }
01223 
01224 
01225       const std::list<Package::Ptr> & RpmDb::doGetPackages(callback::SendReport<ScanDBReport> & report)
01226       {
01227         if ( packagesValid() )
01228           {
01229             return _packages._list;
01230           }
01231 
01232         _packages.clear();
01233 
01235         // Collect package data.
01237         unsigned expect = 0;
01238         librpmDb::constPtr dbptr;
01239         librpmDb::dbAccess( dbptr );
01240         expect = dbptr->size();
01241         DBG << "Expecting " << expect << " packages" << endl;
01242 
01243         librpmDb::db_const_iterator iter;
01244         unsigned current = 0;
01245         CapFactory _f;
01246         Pathname location;
01247 
01248         for ( iter.findAll(); *iter; ++iter, ++current, report->progress( (100*current)/expect))
01249           {
01250 
01251             string name = iter->tag_name();
01252             if ( name == string( "gpg-pubkey" ) )
01253               {
01254                 DBG << "Ignoring pseudo package " << name << endl;
01255                 // pseudo package filtered, as we can't handle multiple instances
01256                 // of 'gpg-pubkey-VERS-REL'.
01257                 continue;
01258               }
01259 
01260             Package::Ptr pptr = makePackageFromHeader( *iter, &_filerequires, location, Source_Ref() );
01261             if ( ! pptr )
01262               {
01263                 WAR << "Failed to make package from database header '" << name << "'" << endl;
01264                 continue;
01265               }
01266 
01267             _packages._list.push_back( pptr );
01268           }
01269         _packages.buildIndex();
01270         DBG << "Found installed packages: " << _packages._list.size() << endl;
01271 
01273         // Evaluate filerequires collected so far
01275         for ( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it )
01276           {
01277 
01278             for ( iter.findByFile( *it ); *iter; ++iter )
01279               {
01280                 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
01281                 if ( !pptr )
01282                   {
01283                     WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
01284                     continue;
01285                   }
01286                 pptr->injectProvides(_f.parse(ResTraits<Package>::kind, *it));
01287               }
01288 
01289           }
01290 
01292         // Build final packages list
01294         return _packages._list;
01295       }
01296 
01298       //
01299       //
01300       //        METHOD NAME : RpmDb::fileList
01301       //        METHOD TYPE : bool
01302       //
01303       //        DESCRIPTION :
01304       //
01305       std::list<FileInfo>
01306       RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
01307       {
01308         std::list<FileInfo> result;
01309 
01310         librpmDb::db_const_iterator it;
01311         bool found;
01312         if (edition_r == Edition::noedition)
01313           {
01314             found = it.findPackage( name_r );
01315           }
01316         else
01317           {
01318             found = it.findPackage( name_r, edition_r );
01319           }
01320         if (!found)
01321           return result;
01322 
01323         return result;
01324       }
01325 
01326 
01328       //
01329       //
01330       //        METHOD NAME : RpmDb::hasFile
01331       //        METHOD TYPE : bool
01332       //
01333       //        DESCRIPTION :
01334       //
01335       bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
01336       {
01337         librpmDb::db_const_iterator it;
01338         bool res;
01339         do
01340         {
01341           res = it.findByFile( file_r );
01342           if (!res) break;
01343           if (!name_r.empty())
01344             {
01345               res = (it->tag_name() == name_r);
01346             }
01347           ++it;
01348         }
01349         while (res && *it);
01350         return res;
01351       }
01352 
01354       //
01355       //
01356       //        METHOD NAME : RpmDb::whoOwnsFile
01357       //        METHOD TYPE : string
01358       //
01359       //        DESCRIPTION :
01360       //
01361       std::string RpmDb::whoOwnsFile( const std::string & file_r) const
01362       {
01363         librpmDb::db_const_iterator it;
01364         if (it.findByFile( file_r ))
01365           {
01366             return it->tag_name();
01367           }
01368         return "";
01369       }
01370 
01372       //
01373       //
01374       //        METHOD NAME : RpmDb::hasProvides
01375       //        METHOD TYPE : bool
01376       //
01377       //        DESCRIPTION :
01378       //
01379       bool RpmDb::hasProvides( const std::string & tag_r ) const
01380       {
01381         librpmDb::db_const_iterator it;
01382         return it.findByProvides( tag_r );
01383       }
01384 
01386       //
01387       //
01388       //        METHOD NAME : RpmDb::hasRequiredBy
01389       //        METHOD TYPE : bool
01390       //
01391       //        DESCRIPTION :
01392       //
01393       bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
01394       {
01395         librpmDb::db_const_iterator it;
01396         return it.findByRequiredBy( tag_r );
01397       }
01398 
01400       //
01401       //
01402       //        METHOD NAME : RpmDb::hasConflicts
01403       //        METHOD TYPE : bool
01404       //
01405       //        DESCRIPTION :
01406       //
01407       bool RpmDb::hasConflicts( const std::string & tag_r ) const
01408       {
01409         librpmDb::db_const_iterator it;
01410         return it.findByConflicts( tag_r );
01411       }
01412 
01414       //
01415       //
01416       //        METHOD NAME : RpmDb::hasPackage
01417       //        METHOD TYPE : bool
01418       //
01419       //        DESCRIPTION :
01420       //
01421       bool RpmDb::hasPackage( const string & name_r ) const
01422       {
01423         librpmDb::db_const_iterator it;
01424         return it.findPackage( name_r );
01425       }
01426 
01428       //
01429       //
01430       //        METHOD NAME : RpmDb::hasPackage
01431       //        METHOD TYPE : bool
01432       //
01433       //        DESCRIPTION :
01434       //
01435       bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
01436       {
01437         librpmDb::db_const_iterator it;
01438         return it.findPackage( name_r, ed_r );
01439       }
01440 
01442       //
01443       //
01444       //        METHOD NAME : RpmDb::getData
01445       //        METHOD TYPE : PMError
01446       //
01447       //        DESCRIPTION :
01448       //
01449       void RpmDb::getData( const string & name_r,
01450                            RpmHeader::constPtr & result_r ) const
01451       {
01452         librpmDb::db_const_iterator it;
01453         it.findPackage( name_r );
01454         result_r = *it;
01455         if (it.dbError())
01456           ZYPP_THROW(*(it.dbError()));
01457       }
01458 
01460       //
01461       //
01462       //        METHOD NAME : RpmDb::getData
01463       //        METHOD TYPE : void
01464       //
01465       //        DESCRIPTION :
01466       //
01467       void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
01468                            RpmHeader::constPtr & result_r ) const
01469       {
01470         librpmDb::db_const_iterator it;
01471         it.findPackage( name_r, ed_r  );
01472         result_r = *it;
01473         if (it.dbError())
01474           ZYPP_THROW(*(it.dbError()));
01475       }
01476 
01478       //
01479       //        METHOD NAME : RpmDb::checkPackage
01480       //        METHOD TYPE : RpmDb::checkPackageResult
01481       //
01482       RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
01483       {
01484         PathInfo file( path_r );
01485         if ( ! file.isFile() ) {
01486           ERR << "Not a file: " << file << endl;
01487           return CHK_ERROR;
01488         }
01489 
01490         FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
01491         if ( fd == 0 || ::Ferror(fd) ) {
01492           ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
01493           if ( fd )
01494             ::Fclose( fd );
01495           return CHK_ERROR;
01496         }
01497 
01498         rpmts ts = ::rpmtsCreate();
01499         ::rpmtsSetRootDir( ts, root().asString().c_str() );
01500         ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
01501         int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
01502         ts = ::rpmtsFree(ts);
01503 
01504         ::Fclose( fd );
01505 
01506         switch ( res )
01507           {
01508           case RPMRC_OK:
01509             return CHK_OK;
01510             break;
01511           case RPMRC_NOTFOUND:
01512             WAR << "Signature is unknown type. " << file << endl;
01513             return CHK_NOTFOUND;
01514             break;
01515           case RPMRC_FAIL:
01516             WAR << "Signature does not verify. " << file << endl;
01517             return CHK_FAIL;
01518             break;
01519           case RPMRC_NOTTRUSTED:
01520             WAR << "Signature is OK, but key is not trusted. " << file << endl;
01521             return CHK_NOTTRUSTED;
01522             break;
01523           case RPMRC_NOKEY:
01524             WAR << "Public key is unavailable. " << file << endl;
01525             return CHK_NOKEY;
01526             break;
01527           }
01528         ERR << "Error reading header." << file << endl;
01529         return CHK_ERROR;
01530       }
01531 
01532       // determine changed files of installed package
01533       bool
01534       RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
01535       {
01536         bool ok = true;
01537 
01538         fileList.clear();
01539 
01540         if ( ! initialized() ) return false;
01541 
01542         RpmArgVec opts;
01543 
01544         opts.push_back ("-V");
01545         opts.push_back ("--nodeps");
01546         opts.push_back ("--noscripts");
01547         opts.push_back ("--nomd5");
01548         opts.push_back ("--");
01549         opts.push_back (packageName.c_str());
01550 
01551         run_rpm (opts, ExternalProgram::Discard_Stderr);
01552 
01553         if ( process == NULL )
01554           return false;
01555 
01556         /* from rpm manpage
01557          5      MD5 sum
01558          S      File size
01559          L      Symlink
01560          T      Mtime
01561          D      Device
01562          U      User
01563          G      Group
01564          M      Mode (includes permissions and file type)
01565         */
01566 
01567         string line;
01568         while (systemReadLine(line))
01569           {
01570             if (line.length() > 12 &&
01571                  (line[0] == 'S' || line[0] == 's' ||
01572                    (line[0] == '.' && line[7] == 'T')))
01573               {
01574                 // file has been changed
01575                 string filename;
01576 
01577                 filename.assign(line, 11, line.length() - 11);
01578                 fileList.insert(filename);
01579               }
01580           }
01581 
01582         systemStatus();
01583         // exit code ignored, rpm returns 1 no matter if package is installed or
01584         // not
01585 
01586         return ok;
01587       }
01588 
01589 
01590 
01591       /****************************************************************/
01592       /* private member-functions                                       */
01593       /****************************************************************/
01594 
01595       /*--------------------------------------------------------------*/
01596       /* Run rpm with the specified arguments, handling stderr  */
01597       /* as specified  by disp                                  */
01598       /*--------------------------------------------------------------*/
01599       void
01600       RpmDb::run_rpm (const RpmArgVec& opts,
01601                        ExternalProgram::Stderr_Disposition disp)
01602       {
01603         if ( process )
01604           {
01605             delete process;
01606             process = NULL;
01607           }
01608         exit_code = -1;
01609 
01610         if ( ! initialized() )
01611           {
01612             ZYPP_THROW(RpmDbNotOpenException());
01613           }
01614 
01615         RpmArgVec args;
01616 
01617         // always set root and dbpath
01618         args.push_back("rpm");
01619         args.push_back("--root");
01620         args.push_back(_root.asString().c_str());
01621         args.push_back("--dbpath");
01622         args.push_back(_dbPath.asString().c_str());
01623 
01624         const char* argv[args.size() + opts.size() + 1];
01625 
01626         const char** p = argv;
01627         p = copy (args.begin (), args.end (), p);
01628         p = copy (opts.begin (), opts.end (), p);
01629         *p = 0;
01630 
01631         // Invalidate all outstanding database handles in case
01632         // the database gets modified.
01633         librpmDb::dbRelease( true );
01634 
01635         // Launch the program with default locale
01636         process = new ExternalProgram(argv, disp, false, -1, true);
01637         return;
01638       }
01639 
01640       /*--------------------------------------------------------------*/
01641       /* Read a line from the rpm process                               */
01642       /*--------------------------------------------------------------*/
01643       bool
01644       RpmDb::systemReadLine(string &line)
01645       {
01646         line.erase();
01647 
01648         if ( process == NULL )
01649           return false;
01650 
01651         line = process->receiveLine();
01652 
01653         if (line.length() == 0)
01654           return false;
01655 
01656         if (line[line.length() - 1] == '\n')
01657           line.erase(line.length() - 1);
01658 
01659         return true;
01660       }
01661 
01662       /*--------------------------------------------------------------*/
01663       /* Return the exit status of the rpm process, closing the */
01664       /* connection if not already done                         */
01665       /*--------------------------------------------------------------*/
01666       int
01667       RpmDb::systemStatus()
01668       {
01669         if ( process == NULL )
01670           return -1;
01671 
01672         exit_code = process->close();
01673         process->kill();
01674         delete process;
01675         process = 0;
01676 
01677         //   DBG << "exit code " << exit_code << endl;
01678 
01679         return exit_code;
01680       }
01681 
01682       /*--------------------------------------------------------------*/
01683       /* Forcably kill the rpm process                          */
01684       /*--------------------------------------------------------------*/
01685       void
01686       RpmDb::systemKill()
01687       {
01688         if (process) process->kill();
01689       }
01690 
01691 
01692       // generate diff mails for config files
01693       void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
01694       {
01695         string msg = line.substr(9);
01696         string::size_type pos1 = string::npos;
01697         string::size_type pos2 = string::npos;
01698         string file1s, file2s;
01699         Pathname file1;
01700         Pathname file2;
01701 
01702         pos1 = msg.find (typemsg);
01703         for (;;)
01704           {
01705             if ( pos1 == string::npos )
01706               break;
01707 
01708             pos2 = pos1 + strlen (typemsg);
01709 
01710             if (pos2 >= msg.length() )
01711               break;
01712 
01713             file1 = msg.substr (0, pos1);
01714             file2 = msg.substr (pos2);
01715 
01716             file1s = file1.asString();
01717             file2s = file2.asString();
01718 
01719             if (!_root.empty() && _root != "/")
01720               {
01721                 file1 = _root + file1;
01722                 file2 = _root + file2;
01723               }
01724 
01725             string out;
01726             int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
01727             if (ret)
01728               {
01729                 Pathname file = _root + WARNINGMAILPATH;
01730                 if (filesystem::assert_dir(file) != 0)
01731                   {
01732                     ERR << "Could not create " << file.asString() << endl;
01733                     break;
01734                   }
01735                 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
01736                 ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
01737                 if (!notify)
01738                   {
01739                     ERR << "Could not open " <<  file << endl;
01740                     break;
01741                   }
01742 
01743                 // Translator: %s = name of an rpm package. A list of diffs follows
01744                 // this message.
01745                 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
01746                 if (ret>1)
01747                   {
01748                     ERR << "diff failed" << endl;
01749                     notify << str::form(difffailmsg,
01750                                          file1s.c_str(), file2s.c_str()) << endl;
01751                   }
01752                 else
01753                   {
01754                     notify << str::form(diffgenmsg,
01755                                          file1s.c_str(), file2s.c_str()) << endl;
01756 
01757                     // remove root for the viewer's pleasure (#38240)
01758                     if (!_root.empty() && _root != "/")
01759                       {
01760                         if (out.substr(0,4) == "--- ")
01761                           {
01762                             out.replace(4, file1.asString().length(), file1s);
01763                           }
01764                         string::size_type pos = out.find("\n+++ ");
01765                         if (pos != string::npos)
01766                           {
01767                             out.replace(pos+5, file2.asString().length(), file2s);
01768                           }
01769                       }
01770                     notify << out << endl;
01771                   }
01772                 notify.close();
01773                 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
01774                 notify.close();
01775               }
01776             else
01777               {
01778                 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
01779               }
01780             break;
01781           }
01782       }
01783 
01785       //
01786       //
01787       //        METHOD NAME : RpmDb::installPackage
01788       //        METHOD TYPE : PMError
01789       //
01790       void RpmDb::installPackage( const Pathname & filename, unsigned flags )
01791       {
01792         callback::SendReport<RpmInstallReport> report;
01793 
01794         report->start(filename);
01795 
01796         do
01797         try
01798           {
01799             doInstallPackage(filename, flags, report);
01800             report->finish();
01801             break;
01802           }
01803         catch (RpmException & excpt_r)
01804           {
01805             RpmInstallReport::Action user = report->problem( excpt_r );
01806 
01807             if ( user == RpmInstallReport::ABORT )
01808               {
01809                 report->finish( excpt_r );
01810                 ZYPP_RETHROW(excpt_r);
01811               }
01812             else if ( user == RpmInstallReport::IGNORE )
01813               {
01814                 break;
01815               }
01816           }
01817         while (true);
01818       }
01819 
01820       void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callback::SendReport<RpmInstallReport> & report )
01821       {
01822         FAILIFNOTINITIALIZED;
01823         CommitLog progresslog;
01824 
01825         MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
01826 
01827 
01828         // backup
01829         if ( _packagebackups )
01830           {
01831             // FIXME      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
01832             if ( ! backupPackage( filename ) )
01833               {
01834                 ERR << "backup of " << filename.asString() << " failed" << endl;
01835               }
01836             // FIXME status handling
01837             report->progress( 0 ); // allow 1% for backup creation.
01838           }
01839         else
01840           {
01841             report->progress( 100 );
01842           }
01843 
01844         // run rpm
01845         RpmArgVec opts;
01846         if (flags & RPMINST_NOUPGRADE)
01847           opts.push_back("-i");
01848         else
01849           opts.push_back("-U");
01850         opts.push_back("--percent");
01851 
01852         if (flags & RPMINST_NODIGEST)
01853           opts.push_back("--nodigest");
01854         if (flags & RPMINST_NOSIGNATURE)
01855           opts.push_back("--nosignature");
01856         if (flags & RPMINST_NODOCS)
01857           opts.push_back ("--excludedocs");
01858         if (flags & RPMINST_NOSCRIPTS)
01859           opts.push_back ("--noscripts");
01860         if (flags & RPMINST_FORCE)
01861           opts.push_back ("--force");
01862         if (flags & RPMINST_NODEPS)
01863           opts.push_back ("--nodeps");
01864         if (flags & RPMINST_IGNORESIZE)
01865           opts.push_back ("--ignoresize");
01866         if (flags & RPMINST_JUSTDB)
01867           opts.push_back ("--justdb");
01868         if (flags & RPMINST_TEST)
01869           opts.push_back ("--test");
01870 
01871         opts.push_back("--");
01872 
01873         // rpm requires additional quoting of special chars:
01874         std::string quotedFilename( rpmQuoteFilename( filename ) );
01875         opts.push_back ( quotedFilename.c_str() );
01876 
01877         modifyDatabase(); // BEFORE run_rpm
01878         run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01879 
01880         string line;
01881         string rpmmsg;
01882         vector<string> configwarnings;
01883         vector<string> errorlines;
01884 
01885         while (systemReadLine(line))
01886           {
01887             if (line.substr(0,2)=="%%")
01888               {
01889                 int percent;
01890                 sscanf (line.c_str () + 2, "%d", &percent);
01891                 report->progress( percent );
01892               }
01893             else
01894               rpmmsg += line+'\n';
01895 
01896             if ( line.substr(0,8) == "warning:" )
01897               {
01898                 configwarnings.push_back(line);
01899               }
01900           }
01901         int rpm_status = systemStatus();
01902 
01903         // evaluate result
01904         for (vector<string>::iterator it = configwarnings.begin();
01905               it != configwarnings.end(); ++it)
01906           {
01907             processConfigFiles(*it, Pathname::basename(filename), " saved as ",
01908                                 // %s = filenames
01909                                 _("rpm saved %s as %s but it was impossible to determine the difference"),
01910                                 // %s = filenames
01911                                 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
01912             processConfigFiles(*it, Pathname::basename(filename), " created as ",
01913                                 // %s = filenames
01914                                 _("rpm created %s as %s but it was impossible to determine the difference"),
01915                                 // %s = filenames
01916                                 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
01917           }
01918 
01919         if ( rpm_status != 0 )
01920           {
01921             // %s = filename of rpm package
01922             progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
01923             progresslog() << _("rpm output:") << endl << rpmmsg << endl;
01924             ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
01925           }
01926         else
01927           {
01928             // %s = filename of rpm package
01929             progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
01930             if ( ! rpmmsg.empty() )
01931               {
01932                 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
01933               }
01934           }
01935       }
01936 
01938       //
01939       //
01940       //        METHOD NAME : RpmDb::removePackage
01941       //        METHOD TYPE : PMError
01942       //
01943       void RpmDb::removePackage( Package::constPtr package, unsigned flags )
01944       {
01945         // clear epoch, see bnc #372323
01946         Edition tmp(package->edition().version(), package->edition().release(), Edition::noepoch); 
01947 
01948         return removePackage( package->name()
01949                               + "-" + tmp.asString()
01950                               + "." + package->arch().asString(), flags );
01951       }
01952 
01954       //
01955       //
01956       //        METHOD NAME : RpmDb::removePackage
01957       //        METHOD TYPE : PMError
01958       //
01959       void RpmDb::removePackage( const string & name_r, unsigned flags )
01960       {
01961         callback::SendReport<RpmRemoveReport> report;
01962 
01963         report->start( name_r );
01964 
01965         try
01966           {
01967             doRemovePackage(name_r, flags, report);
01968           }
01969         catch (RpmException & excpt_r)
01970           {
01971             report->finish(excpt_r);
01972             ZYPP_RETHROW(excpt_r);
01973           }
01974         report->finish();
01975       }
01976 
01977 
01978       void RpmDb::doRemovePackage( const string & name_r, unsigned flags, callback::SendReport<RpmRemoveReport> & report )
01979       {
01980         FAILIFNOTINITIALIZED;
01981         CommitLog progresslog;
01982 
01983         MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
01984 
01985         // backup
01986         if ( _packagebackups )
01987           {
01988             // FIXME solve this status report somehow
01989             //      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
01990             if ( ! backupPackage( name_r ) )
01991               {
01992                 ERR << "backup of " << name_r << " failed" << endl;
01993               }
01994             report->progress( 0 );
01995           }
01996         else
01997           {
01998             report->progress( 100 );
01999           }
02000 
02001         // run rpm
02002         RpmArgVec opts;
02003         opts.push_back("-e");
02004         opts.push_back("--allmatches");
02005 
02006         if (flags & RPMINST_NOSCRIPTS)
02007           opts.push_back("--noscripts");
02008         if (flags & RPMINST_NODEPS)
02009           opts.push_back("--nodeps");
02010         if (flags & RPMINST_JUSTDB)
02011           opts.push_back("--justdb");
02012         if (flags & RPMINST_TEST)
02013           opts.push_back ("--test");
02014         if (flags & RPMINST_FORCE)
02015           {
02016             WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
02017           }
02018 
02019         opts.push_back("--");
02020         opts.push_back(name_r.c_str());
02021 
02022         modifyDatabase(); // BEFORE run_rpm
02023         run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
02024 
02025         string line;
02026         string rpmmsg;
02027 
02028         // got no progress from command, so we fake it:
02029         // 5  - command started
02030         // 50 - command completed
02031         // 100 if no error
02032         report->progress( 5 );
02033         while (systemReadLine(line))
02034           {
02035             rpmmsg += line+'\n';
02036           }
02037         report->progress( 50 );
02038         int rpm_status = systemStatus();
02039 
02040         if ( rpm_status != 0 )
02041           {
02042             // %s = name of rpm package
02043             progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
02044             progresslog() << _("rpm output:") << endl << rpmmsg << endl;
02045             ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
02046           }
02047         else
02048           {
02049             progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
02050             if ( ! rpmmsg.empty() )
02051               {
02052                 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
02053               }
02054           }
02055       }
02056 
02058       //
02059       //
02060       //        METHOD NAME : RpmDb::backupPackage
02061       //        METHOD TYPE : bool
02062       //
02063       bool RpmDb::backupPackage( const Pathname & filename )
02064       {
02065         RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
02066         if ( ! h )
02067           return false;
02068 
02069         return backupPackage( h->tag_name() );
02070       }
02071 
02073       //
02074       //
02075       //        METHOD NAME : RpmDb::backupPackage
02076       //        METHOD TYPE : bool
02077       //
02078       bool RpmDb::backupPackage(const string& packageName)
02079       {
02080         CommitLog progresslog;
02081         bool ret = true;
02082         Pathname backupFilename;
02083         Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
02084 
02085         if (_backuppath.empty())
02086           {
02087             INT << "_backuppath empty" << endl;
02088             return false;
02089           }
02090 
02091         FileList fileList;
02092 
02093         if (!queryChangedFiles(fileList, packageName))
02094           {
02095             ERR << "Error while getting changed files for package " <<
02096             packageName << endl;
02097             return false;
02098           }
02099 
02100         if (fileList.size() <= 0)
02101           {
02102             DBG <<  "package " <<  packageName << " not changed -> no backup" << endl;
02103             return true;
02104           }
02105 
02106         if (filesystem::assert_dir(_root + _backuppath) != 0)
02107           {
02108             return false;
02109           }
02110 
02111         {
02112           // build up archive name
02113           time_t currentTime = time(0);
02114           struct tm *currentLocalTime = localtime(&currentTime);
02115 
02116           int date = (currentLocalTime->tm_year + 1900) * 10000
02117           + (currentLocalTime->tm_mon + 1) * 100
02118           + currentLocalTime->tm_mday;
02119 
02120           int num = 0;
02121           do
02122           {
02123             backupFilename = _root + _backuppath
02124             + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
02125 
02126           }
02127           while ( PathInfo(backupFilename).isExist() && num++ < 1000);
02128 
02129           PathInfo pi(filestobackupfile);
02130           if (pi.isExist() && !pi.isFile())
02131             {
02132               ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
02133               return false;
02134             }
02135 
02136           std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
02137 
02138           if (!fp)
02139             {
02140               ERR << "could not open " << filestobackupfile.asString() << endl;
02141               return false;
02142             }
02143 
02144           for (FileList::const_iterator cit = fileList.begin();
02145                 cit != fileList.end(); ++cit)
02146             {
02147               string name = *cit;
02148               if ( name[0] == '/' )
02149                 {
02150                   // remove slash, file must be relative to -C parameter of tar
02151                   name = name.substr( 1 );
02152                 }
02153               DBG << "saving file "<< name << endl;
02154               fp << name << endl;
02155             }
02156           fp.close();
02157 
02158           const char* const argv[] =
02159           {
02160             "tar",
02161             "-czhP",
02162             "-C",
02163             _root.asString().c_str(),
02164             "--ignore-failed-read",
02165             "-f",
02166             backupFilename.asString().c_str(),
02167             "-T",
02168             filestobackupfile.asString().c_str(),
02169             NULL
02170           };
02171 
02172           // execute tar in inst-sys (we dont know if there is a tar below _root !)
02173           ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
02174 
02175           string tarmsg;
02176 
02177           // TODO: its probably possible to start tar with -v and watch it adding
02178           // files to report progress
02179           for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
02180             {
02181               tarmsg+=output;
02182             }
02183 
02184           int ret = tar.close();
02185 
02186           if ( ret != 0)
02187             {
02188               ERR << "tar failed: " << tarmsg << endl;
02189               ret = false;
02190             }
02191           else
02192             {
02193               MIL << "tar backup ok" << endl;
02194               progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
02195             }
02196 
02197           filesystem::unlink(filestobackupfile);
02198         }
02199 
02200           return ret;
02201       }
02202 
02203       void RpmDb::setBackupPath(const Pathname& path)
02204       {
02205         _backuppath = path;
02206       }
02207 
02208     } // namespace rpm
02209   } // namespace target
02210 } // namespace zypp

Generated on Thu Apr 24 02:24:59 2008 for zypp by  doxygen 1.4.6