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

Generated on Tue Oct 21 02:33:02 2008 for libzypp by  doxygen 1.5.3