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).path());
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   for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
00993   {
00994     string id = str::toUpper( (*it).version() );
00995     string keyshortid = pubkey_r.id().substr(8,8);
00996     MIL << "Comparing '" << id << "' to '" << keyshortid << "'" << endl;
00997     if ( id == keyshortid )
00998     {
00999       // they match id
01000       // FIXME id is not sufficient?
01001       MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring." << endl;
01002       return;
01003     }
01004   }
01005   // key does not exists, lets import it
01006 
01007   RpmArgVec opts;
01008   opts.push_back ( "--import" );
01009   opts.push_back ( "--" );
01010   opts.push_back ( pubkey_r.path().asString().c_str() );
01011 
01012   // don't call modifyDatabase because it would remove the old
01013   // rpm3 database, if the current database is a temporary one.
01014   // But do invalidate packages list.
01015   _packages._valid = false;
01016   run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01017 
01018   string line;
01019   while ( systemReadLine( line ) )
01020   {
01021     if ( line.substr( 0, 6 ) == "error:" )
01022     {
01023       WAR << line << endl;
01024     }
01025     else
01026     {
01027       DBG << line << endl;
01028     }
01029   }
01030 
01031   int rpm_status = systemStatus();
01032 
01033   if ( rpm_status != 0 )
01034   {
01035     ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.asString() + string(": rpm returned  ") + str::numstring(rpm_status)));
01036   }
01037   else
01038   {
01039     MIL << "Key " << pubkey_r << " imported in rpm trusted keyring." << endl;
01040   }
01041 }
01042 
01044 //
01045 //
01046 //      METHOD NAME : RpmDb::pubkeys
01047 //      METHOD TYPE : set<Edition>
01048 //
01049 list<PublicKey> RpmDb::pubkeys() const
01050 {
01051   list<PublicKey> ret;
01052 
01053   librpmDb::db_const_iterator it;
01054   for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
01055   {
01056     Edition edition = it->tag_edition();
01057     if (edition != Edition::noedition)
01058     {
01059       // we export the rpm key into a file
01060       RpmHeader::constPtr result = new RpmHeader();
01061       getData( string("gpg-pubkey"), edition, result );
01062       TmpFile file(getZYpp()->tmpPath());
01063       ofstream os;
01064       try
01065       {
01066         os.open(file.path().asString().c_str());
01067         // dump rpm key into the tmp file
01068         os << result->tag_description();
01069         //MIL << "-----------------------------------------------" << endl;
01070         //MIL << result->tag_description() <<endl;
01071         //MIL << "-----------------------------------------------" << endl;
01072         os.close();
01073         // read the public key from the dumped file
01074         PublicKey key(file.path());
01075         ret.push_back(key);
01076       }
01077       catch (exception &e)
01078       {
01079         ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << endl;
01080         // just ignore the key
01081       }
01082     }
01083   }
01084   return ret;
01085 }
01086 
01087 set<Edition> RpmDb::pubkeyEditions() const
01088   {
01089     set<Edition> ret;
01090 
01091     librpmDb::db_const_iterator it;
01092     for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
01093     {
01094       Edition edition = it->tag_edition();
01095       if (edition != Edition::noedition)
01096         ret.insert( edition );
01097     }
01098     return ret;
01099   }
01100 
01102 //
01103 //
01104 //      METHOD NAME : RpmDb::packagesValid
01105 //      METHOD TYPE : bool
01106 //
01107 bool RpmDb::packagesValid() const
01108 {
01109   return( _packages._valid || ! initialized() );
01110 }
01111 
01113 //
01114 //
01115 //      METHOD NAME : RpmDb::getPackages
01116 //      METHOD TYPE : const list<Package::Ptr> &
01117 //
01118 //      DESCRIPTION :
01119 //
01120 const list<Package::Ptr> & RpmDb::getPackages()
01121 {
01122   callback::SendReport<ScanDBReport> report;
01123 
01124   report->start ();
01125 
01126   try
01127   {
01128     const list<Package::Ptr> & ret = doGetPackages(report);
01129     report->finish(ScanDBReport::NO_ERROR, "");
01130     return ret;
01131   }
01132   catch (RpmException & excpt_r)
01133   {
01134     report->finish(ScanDBReport::FAILED, excpt_r.asUserString ());
01135     ZYPP_RETHROW(excpt_r);
01136   }
01137 #warning fixme
01138   static const list<Package::Ptr> empty_list;
01139   return empty_list;
01140 }
01141 
01142 inline static void insertCaps( CapSet &capset, capability::CapabilityImplPtrSet ptrset, CapFactory &factory )
01143 {
01144   for ( capability::CapabilityImplPtrSet::const_iterator it = ptrset.begin();
01145         it != ptrset.end();
01146         ++it )
01147   {
01148     capset.insert( factory.fromImpl(*it) );
01149   }
01150 }
01151 
01152 //
01153 // make Package::Ptr from RpmHeader
01154 // return NULL on error
01155 //
01156 Package::Ptr RpmDb::makePackageFromHeader( const RpmHeader::constPtr header,
01157                                            set<string> * filerequires,
01158                                            const Pathname & location, Repository repo )
01159 {
01160   if ( ! header )
01161     return 0;
01162 
01163   if ( header->isSrc() )
01164   {
01165     WAR << "Can't make Package from SourcePackage header" << endl;
01166     return 0;
01167   }
01168 
01169   Package::Ptr pptr;
01170 
01171   string name = header->tag_name();
01172 
01173   // create dataprovider
01174   detail::ResImplTraits<RPMPackageImpl>::Ptr impl( new RPMPackageImpl( header ) );
01175 
01176   impl->setRepository( repo );
01177   if (!location.empty())
01178     impl->setLocation( OnMediaLocation(location,1) );
01179 
01180   Edition edition;
01181   try
01182   {
01183     edition = Edition( header->tag_version(),
01184                        header->tag_release(),
01185                        header->tag_epoch());
01186   }
01187   catch (Exception & excpt_r)
01188   {
01189     ZYPP_CAUGHT( excpt_r );
01190     WAR << "Package " << name << " has bad edition '"
01191     << (header->tag_epoch().empty()?"":(header->tag_epoch()+":"))
01192     << header->tag_version()
01193     << (header->tag_release().empty()?"":(string("-") + header->tag_release())) << "'";
01194     return pptr;
01195   }
01196 
01197   Arch arch;
01198   try
01199   {
01200     arch = Arch( header->tag_arch() );
01201   }
01202   catch (Exception & excpt_r)
01203   {
01204     ZYPP_CAUGHT( excpt_r );
01205     WAR << "Package " << name << " has bad architecture '" << header->tag_arch() << "'";
01206     return pptr;
01207   }
01208 
01209   // Collect basic Resolvable data
01210   NVRAD dataCollect( header->tag_name(),
01211                      edition,
01212                      arch );
01213 
01214   list<string> filenames = impl->filenames();
01215   CapFactory capfactory;
01216   insertCaps( dataCollect[Dep::PROVIDES], header->tag_provides( filerequires ), capfactory );
01217 
01218   for (list<string>::const_iterator filename = filenames.begin();
01219        filename != filenames.end();
01220        ++filename)
01221   {
01222     if ( capability::isInterestingFileSpec( *filename ) )
01223     {
01224       try
01225       {
01226         dataCollect[Dep::PROVIDES].insert(capfactory.fromImpl(capability::buildFile(ResTraits<Package>::kind, *filename) ));
01227       }
01228       catch (Exception & excpt_r)
01229       {
01230         ZYPP_CAUGHT( excpt_r );
01231         WAR << "Ignoring invalid capability: " << *filename << endl;
01232       }
01233     }
01234   }
01235 
01236   insertCaps( dataCollect[Dep::REQUIRES], header->tag_requires( filerequires ), capfactory );
01237   insertCaps( dataCollect[Dep::PREREQUIRES], header->tag_prerequires( filerequires ), capfactory );
01238   insertCaps( dataCollect[Dep::CONFLICTS], header->tag_conflicts( filerequires ), capfactory );
01239   insertCaps( dataCollect[Dep::OBSOLETES], header->tag_obsoletes( filerequires ), capfactory );
01240   insertCaps( dataCollect[Dep::ENHANCES], header->tag_enhances( filerequires ), capfactory );
01241   insertCaps( dataCollect[Dep::SUPPLEMENTS], header->tag_supplements( filerequires ), capfactory );
01242 
01243   try
01244   {
01245     // create package from dataprovider
01246     pptr = detail::makeResolvableFromImpl( dataCollect, impl );
01247   }
01248   catch (Exception & excpt_r)
01249   {
01250     ZYPP_CAUGHT( excpt_r );
01251     ERR << "Can't create Package::Ptr" << endl;
01252   }
01253 
01254   return pptr;
01255 }
01256 
01257 const list<Package::Ptr> & RpmDb::doGetPackages(callback::SendReport<ScanDBReport> & report)
01258 {
01259   if ( packagesValid() )
01260   {
01261     return _packages._list;
01262   }
01263 
01264   _packages.clear();
01265 
01267   // Collect package data.
01269   unsigned expect = 0;
01270   librpmDb::constPtr dbptr;
01271   librpmDb::dbAccess( dbptr );
01272   expect = dbptr->size();
01273   DBG << "Expecting " << expect << " packages" << endl;
01274 
01275   librpmDb::db_const_iterator iter;
01276   unsigned current = 0;
01277   CapFactory _f;
01278   Pathname location;
01279 
01280   for ( iter.findAll(); *iter; ++iter, ++current, report->progress( (100*current)/expect))
01281   {
01282 
01283     string name = iter->tag_name();
01284     if ( name == string( "gpg-pubkey" ) )
01285     {
01286       DBG << "Ignoring pseudo package " << name << endl;
01287       // pseudo package filtered, as we can't handle multiple instances
01288       // of 'gpg-pubkey-VERS-REL'.
01289       continue;
01290     }
01291 
01292     Package::Ptr pptr = makePackageFromHeader( *iter, &_filerequires, location, Repository() );
01293     if ( ! pptr )
01294     {
01295       WAR << "Failed to make package from database header '" << name << "'" << endl;
01296       continue;
01297     }
01298 
01299     _packages._list.push_back( pptr );
01300   }
01301   _packages.buildIndex();
01302   DBG << "Found installed packages: " << _packages._list.size() << endl;
01303 
01305   // Evaluate filerequires collected so far
01307   for ( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it )
01308     {
01309 
01310       for ( iter.findByFile( *it ); *iter; ++iter )
01311       {
01312         Package::Ptr pptr = _packages.lookup( iter->tag_name() );
01313         if ( !pptr )
01314         {
01315           WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
01316           continue;
01317         }
01318         pptr->injectProvides(_f.parse(ResTraits<Package>::kind, *it));
01319       }
01320 
01321     }
01322 
01324   // Build final packages list
01326   return _packages._list;
01327 }
01328 
01330 //
01331 //
01332 //      METHOD NAME : RpmDb::fileList
01333 //      METHOD TYPE : bool
01334 //
01335 //      DESCRIPTION :
01336 //
01337 list<FileInfo>
01338 RpmDb::fileList( const string & name_r, const Edition & edition_r ) const
01339 {
01340   list<FileInfo> result;
01341 
01342   librpmDb::db_const_iterator it;
01343   bool found;
01344   if (edition_r == Edition::noedition)
01345   {
01346     found = it.findPackage( name_r );
01347   }
01348   else
01349   {
01350     found = it.findPackage( name_r, edition_r );
01351   }
01352   if (!found)
01353     return result;
01354 
01355   return result;
01356 }
01357 
01358 
01360 //
01361 //
01362 //      METHOD NAME : RpmDb::hasFile
01363 //      METHOD TYPE : bool
01364 //
01365 //      DESCRIPTION :
01366 //
01367 bool RpmDb::hasFile( const string & file_r, const string & name_r ) const
01368 {
01369   librpmDb::db_const_iterator it;
01370   bool res;
01371   do
01372   {
01373     res = it.findByFile( file_r );
01374     if (!res) break;
01375     if (!name_r.empty())
01376     {
01377       res = (it->tag_name() == name_r);
01378     }
01379     ++it;
01380   }
01381   while (res && *it);
01382   return res;
01383 }
01384 
01386 //
01387 //
01388 //      METHOD NAME : RpmDb::whoOwnsFile
01389 //      METHOD TYPE : string
01390 //
01391 //      DESCRIPTION :
01392 //
01393 string RpmDb::whoOwnsFile( const string & file_r) const
01394 {
01395   librpmDb::db_const_iterator it;
01396   if (it.findByFile( file_r ))
01397   {
01398     return it->tag_name();
01399   }
01400   return "";
01401 }
01402 
01404 //
01405 //
01406 //      METHOD NAME : RpmDb::hasProvides
01407 //      METHOD TYPE : bool
01408 //
01409 //      DESCRIPTION :
01410 //
01411 bool RpmDb::hasProvides( const string & tag_r ) const
01412 {
01413   librpmDb::db_const_iterator it;
01414   return it.findByProvides( tag_r );
01415 }
01416 
01418 //
01419 //
01420 //      METHOD NAME : RpmDb::hasRequiredBy
01421 //      METHOD TYPE : bool
01422 //
01423 //      DESCRIPTION :
01424 //
01425 bool RpmDb::hasRequiredBy( const string & tag_r ) const
01426 {
01427   librpmDb::db_const_iterator it;
01428   return it.findByRequiredBy( tag_r );
01429 }
01430 
01432 //
01433 //
01434 //      METHOD NAME : RpmDb::hasConflicts
01435 //      METHOD TYPE : bool
01436 //
01437 //      DESCRIPTION :
01438 //
01439 bool RpmDb::hasConflicts( const string & tag_r ) const
01440 {
01441   librpmDb::db_const_iterator it;
01442   return it.findByConflicts( tag_r );
01443 }
01444 
01446 //
01447 //
01448 //      METHOD NAME : RpmDb::hasPackage
01449 //      METHOD TYPE : bool
01450 //
01451 //      DESCRIPTION :
01452 //
01453 bool RpmDb::hasPackage( const string & name_r ) const
01454 {
01455   librpmDb::db_const_iterator it;
01456   return it.findPackage( name_r );
01457 }
01458 
01460 //
01461 //
01462 //      METHOD NAME : RpmDb::hasPackage
01463 //      METHOD TYPE : bool
01464 //
01465 //      DESCRIPTION :
01466 //
01467 bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
01468 {
01469   librpmDb::db_const_iterator it;
01470   return it.findPackage( name_r, ed_r );
01471 }
01472 
01474 //
01475 //
01476 //      METHOD NAME : RpmDb::getData
01477 //      METHOD TYPE : PMError
01478 //
01479 //      DESCRIPTION :
01480 //
01481 void RpmDb::getData( const string & name_r,
01482                      RpmHeader::constPtr & result_r ) const
01483 {
01484   librpmDb::db_const_iterator it;
01485   it.findPackage( name_r );
01486   result_r = *it;
01487   if (it.dbError())
01488     ZYPP_THROW(*(it.dbError()));
01489 }
01490 
01492 //
01493 //
01494 //      METHOD NAME : RpmDb::getData
01495 //      METHOD TYPE : void
01496 //
01497 //      DESCRIPTION :
01498 //
01499 void RpmDb::getData( const string & name_r, const Edition & ed_r,
01500                      RpmHeader::constPtr & result_r ) const
01501 {
01502   librpmDb::db_const_iterator it;
01503   it.findPackage( name_r, ed_r  );
01504   result_r = *it;
01505   if (it.dbError())
01506     ZYPP_THROW(*(it.dbError()));
01507 }
01508 
01510 //
01511 //      METHOD NAME : RpmDb::checkPackage
01512 //      METHOD TYPE : RpmDb::checkPackageResult
01513 //
01514 RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
01515 {
01516   PathInfo file( path_r );
01517   if ( ! file.isFile() )
01518   {
01519     ERR << "Not a file: " << file << endl;
01520     return CHK_ERROR;
01521   }
01522 
01523   FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
01524   if ( fd == 0 || ::Ferror(fd) )
01525   {
01526     ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
01527     if ( fd )
01528       ::Fclose( fd );
01529     return CHK_ERROR;
01530   }
01531 
01532   rpmts ts = ::rpmtsCreate();
01533   ::rpmtsSetRootDir( ts, root().asString().c_str() );
01534   ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
01535   int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
01536   ts = ::rpmtsFree(ts);
01537 
01538   ::Fclose( fd );
01539 
01540   switch ( res )
01541   {
01542   case RPMRC_OK:
01543     return CHK_OK;
01544     break;
01545   case RPMRC_NOTFOUND:
01546     WAR << "Signature is unknown type. " << file << endl;
01547     return CHK_NOTFOUND;
01548     break;
01549   case RPMRC_FAIL:
01550     WAR << "Signature does not verify. " << file << endl;
01551     return CHK_FAIL;
01552     break;
01553   case RPMRC_NOTTRUSTED:
01554     WAR << "Signature is OK, but key is not trusted. " << file << endl;
01555     return CHK_NOTTRUSTED;
01556     break;
01557   case RPMRC_NOKEY:
01558     WAR << "Public key is unavailable. " << file << endl;
01559     return CHK_NOKEY;
01560     break;
01561   }
01562   ERR << "Error reading header." << file << endl;
01563   return CHK_ERROR;
01564 }
01565 
01566 // determine changed files of installed package
01567 bool
01568 RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
01569 {
01570   bool ok = true;
01571 
01572   fileList.clear();
01573 
01574   if ( ! initialized() ) return false;
01575 
01576   RpmArgVec opts;
01577 
01578   opts.push_back ("-V");
01579   opts.push_back ("--nodeps");
01580   opts.push_back ("--noscripts");
01581   opts.push_back ("--nomd5");
01582   opts.push_back ("--");
01583   opts.push_back (packageName.c_str());
01584 
01585   run_rpm (opts, ExternalProgram::Discard_Stderr);
01586 
01587   if ( process == NULL )
01588     return false;
01589 
01590   /* from rpm manpage
01591    5      MD5 sum
01592    S      File size
01593    L      Symlink
01594    T      Mtime
01595    D      Device
01596    U      User
01597    G      Group
01598    M      Mode (includes permissions and file type)
01599   */
01600 
01601   string line;
01602   while (systemReadLine(line))
01603   {
01604     if (line.length() > 12 &&
01605         (line[0] == 'S' || line[0] == 's' ||
01606          (line[0] == '.' && line[7] == 'T')))
01607     {
01608       // file has been changed
01609       string filename;
01610 
01611       filename.assign(line, 11, line.length() - 11);
01612       fileList.insert(filename);
01613     }
01614   }
01615 
01616   systemStatus();
01617   // exit code ignored, rpm returns 1 no matter if package is installed or
01618   // not
01619 
01620   return ok;
01621 }
01622 
01623 
01624 
01625 /****************************************************************/
01626 /* private member-functions                                     */
01627 /****************************************************************/
01628 
01629 /*--------------------------------------------------------------*/
01630 /* Run rpm with the specified arguments, handling stderr        */
01631 /* as specified  by disp                                        */
01632 /*--------------------------------------------------------------*/
01633 void
01634 RpmDb::run_rpm (const RpmArgVec& opts,
01635                 ExternalProgram::Stderr_Disposition disp)
01636 {
01637   if ( process )
01638   {
01639     delete process;
01640     process = NULL;
01641   }
01642   exit_code = -1;
01643 
01644   if ( ! initialized() )
01645   {
01646     ZYPP_THROW(RpmDbNotOpenException());
01647   }
01648 
01649   RpmArgVec args;
01650 
01651   // always set root and dbpath
01652   args.push_back("rpm");
01653   args.push_back("--root");
01654   args.push_back(_root.asString().c_str());
01655   args.push_back("--dbpath");
01656   args.push_back(_dbPath.asString().c_str());
01657 
01658   const char* argv[args.size() + opts.size() + 1];
01659 
01660   const char** p = argv;
01661   p = copy (args.begin (), args.end (), p);
01662   p = copy (opts.begin (), opts.end (), p);
01663   *p = 0;
01664 
01665   // Invalidate all outstanding database handles in case
01666   // the database gets modified.
01667   librpmDb::dbRelease( true );
01668 
01669   // Launch the program with default locale
01670   process = new ExternalProgram(argv, disp, false, -1, true);
01671   return;
01672 }
01673 
01674 /*--------------------------------------------------------------*/
01675 /* Read a line from the rpm process                             */
01676 /*--------------------------------------------------------------*/
01677 bool
01678 RpmDb::systemReadLine(string &line)
01679 {
01680   line.erase();
01681 
01682   if ( process == NULL )
01683     return false;
01684 
01685   line = process->receiveLine();
01686 
01687   if (line.length() == 0)
01688     return false;
01689 
01690   if (line[line.length() - 1] == '\n')
01691     line.erase(line.length() - 1);
01692 
01693   return true;
01694 }
01695 
01696 /*--------------------------------------------------------------*/
01697 /* Return the exit status of the rpm process, closing the       */
01698 /* connection if not already done                               */
01699 /*--------------------------------------------------------------*/
01700 int
01701 RpmDb::systemStatus()
01702 {
01703   if ( process == NULL )
01704     return -1;
01705 
01706   exit_code = process->close();
01707   process->kill();
01708   delete process;
01709   process = 0;
01710 
01711   //   DBG << "exit code " << exit_code << endl;
01712 
01713   return exit_code;
01714 }
01715 
01716 /*--------------------------------------------------------------*/
01717 /* Forcably kill the rpm process                                */
01718 /*--------------------------------------------------------------*/
01719 void
01720 RpmDb::systemKill()
01721 {
01722   if (process) process->kill();
01723 }
01724 
01725 
01726 // generate diff mails for config files
01727 void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
01728 {
01729   string msg = line.substr(9);
01730   string::size_type pos1 = string::npos;
01731   string::size_type pos2 = string::npos;
01732   string file1s, file2s;
01733   Pathname file1;
01734   Pathname file2;
01735 
01736   pos1 = msg.find (typemsg);
01737   for (;;)
01738   {
01739     if ( pos1 == string::npos )
01740       break;
01741 
01742     pos2 = pos1 + strlen (typemsg);
01743 
01744     if (pos2 >= msg.length() )
01745       break;
01746 
01747     file1 = msg.substr (0, pos1);
01748     file2 = msg.substr (pos2);
01749 
01750     file1s = file1.asString();
01751     file2s = file2.asString();
01752 
01753     if (!_root.empty() && _root != "/")
01754     {
01755       file1 = _root + file1;
01756       file2 = _root + file2;
01757     }
01758 
01759     string out;
01760     int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
01761     if (ret)
01762     {
01763       Pathname file = _root + WARNINGMAILPATH;
01764       if (filesystem::assert_dir(file) != 0)
01765       {
01766         ERR << "Could not create " << file.asString() << endl;
01767         break;
01768       }
01769       file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
01770       ofstream notify(file.asString().c_str(), ios::out|ios::app);
01771       if (!notify)
01772       {
01773         ERR << "Could not open " <<  file << endl;
01774         break;
01775       }
01776 
01777       // Translator: %s = name of an rpm package. A list of diffs follows
01778       // this message.
01779       notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
01780       if (ret>1)
01781       {
01782         ERR << "diff failed" << endl;
01783         notify << str::form(difffailmsg,
01784                             file1s.c_str(), file2s.c_str()) << endl;
01785       }
01786       else
01787       {
01788         notify << str::form(diffgenmsg,
01789                             file1s.c_str(), file2s.c_str()) << endl;
01790 
01791         // remove root for the viewer's pleasure (#38240)
01792         if (!_root.empty() && _root != "/")
01793         {
01794           if (out.substr(0,4) == "--- ")
01795           {
01796             out.replace(4, file1.asString().length(), file1s);
01797           }
01798           string::size_type pos = out.find("\n+++ ");
01799           if (pos != string::npos)
01800           {
01801             out.replace(pos+5, file2.asString().length(), file2s);
01802           }
01803         }
01804         notify << out << endl;
01805       }
01806       notify.close();
01807       notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
01808       notify.close();
01809     }
01810     else
01811     {
01812       WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
01813     }
01814     break;
01815   }
01816 }
01817 
01819 //
01820 //
01821 //      METHOD NAME : RpmDb::installPackage
01822 //      METHOD TYPE : PMError
01823 //
01824 void RpmDb::installPackage( const Pathname & filename, unsigned flags )
01825 {
01826   callback::SendReport<RpmInstallReport> report;
01827 
01828   report->start(filename);
01829 
01830   do
01831     try
01832     {
01833       doInstallPackage(filename, flags, report);
01834       report->finish();
01835       break;
01836     }
01837     catch (RpmException & excpt_r)
01838     {
01839       RpmInstallReport::Action user = report->problem( excpt_r );
01840 
01841       if ( user == RpmInstallReport::ABORT )
01842       {
01843         report->finish( excpt_r );
01844         ZYPP_RETHROW(excpt_r);
01845       }
01846       else if ( user == RpmInstallReport::IGNORE )
01847       {
01848         break;
01849       }
01850     }
01851   while (true);
01852 }
01853 
01854 void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callback::SendReport<RpmInstallReport> & report )
01855 {
01856   FAILIFNOTINITIALIZED;
01857   CommitLog progresslog;
01858 
01859   MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
01860 
01861 
01862   // backup
01863   if ( _packagebackups )
01864   {
01865     // FIXME      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
01866     if ( ! backupPackage( filename ) )
01867     {
01868       ERR << "backup of " << filename.asString() << " failed" << endl;
01869     }
01870     // FIXME status handling
01871     report->progress( 0 ); // allow 1% for backup creation.
01872   }
01873   else
01874   {
01875     report->progress( 100 );
01876   }
01877 
01878   // run rpm
01879   RpmArgVec opts;
01880   if (flags & RPMINST_NOUPGRADE)
01881     opts.push_back("-i");
01882   else
01883     opts.push_back("-U");
01884   opts.push_back("--percent");
01885 
01886   if (flags & RPMINST_NODIGEST)
01887     opts.push_back("--nodigest");
01888   if (flags & RPMINST_NOSIGNATURE)
01889     opts.push_back("--nosignature");
01890   if (flags & RPMINST_NODOCS)
01891     opts.push_back ("--excludedocs");
01892   if (flags & RPMINST_NOSCRIPTS)
01893     opts.push_back ("--noscripts");
01894   if (flags & RPMINST_FORCE)
01895     opts.push_back ("--force");
01896   if (flags & RPMINST_NODEPS)
01897     opts.push_back ("--nodeps");
01898   if (flags & RPMINST_IGNORESIZE)
01899     opts.push_back ("--ignoresize");
01900   if (flags & RPMINST_JUSTDB)
01901     opts.push_back ("--justdb");
01902   if (flags & RPMINST_TEST)
01903     opts.push_back ("--test");
01904 
01905   opts.push_back("--");
01906 
01907   // rpm requires additional quoting of special chars:
01908   string quotedFilename( rpmQuoteFilename( filename ) );
01909   opts.push_back ( quotedFilename.c_str() );
01910 
01911   modifyDatabase(); // BEFORE run_rpm
01912   run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01913 
01914   string line;
01915   string rpmmsg;
01916   vector<string> configwarnings;
01917   vector<string> errorlines;
01918 
01919   while (systemReadLine(line))
01920   {
01921     if (line.substr(0,2)=="%%")
01922     {
01923       int percent;
01924       sscanf (line.c_str () + 2, "%d", &percent);
01925       report->progress( percent );
01926     }
01927     else
01928       rpmmsg += line+'\n';
01929 
01930     if ( line.substr(0,8) == "warning:" )
01931     {
01932       configwarnings.push_back(line);
01933     }
01934   }
01935   int rpm_status = systemStatus();
01936 
01937   // evaluate result
01938   for (vector<string>::iterator it = configwarnings.begin();
01939        it != configwarnings.end(); ++it)
01940   {
01941     processConfigFiles(*it, Pathname::basename(filename), " saved as ",
01942                        // %s = filenames
01943                        _("rpm saved %s as %s, but it was impossible to determine the difference"),
01944                        // %s = filenames
01945                        _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
01946     processConfigFiles(*it, Pathname::basename(filename), " created as ",
01947                        // %s = filenames
01948                        _("rpm created %s as %s, but it was impossible to determine the difference"),
01949                        // %s = filenames
01950                        _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
01951   }
01952 
01953   if ( rpm_status != 0 )
01954   {
01955     // %s = filename of rpm package
01956     progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
01957     progresslog() << _("rpm output:") << endl << rpmmsg << endl;
01958     ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
01959   }
01960   else
01961   {
01962     // %s = filename of rpm package
01963     progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
01964     if ( ! rpmmsg.empty() )
01965     {
01966       progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
01967     }
01968   }
01969 }
01970 
01972 //
01973 //
01974 //      METHOD NAME : RpmDb::removePackage
01975 //      METHOD TYPE : PMError
01976 //
01977 void RpmDb::removePackage( Package::constPtr package, unsigned flags )
01978 {
01979   return removePackage( package->name()
01980                         + "-" + package->edition().asString()
01981                         + "." + package->arch().asString(), flags );
01982 }
01983 
01985 //
01986 //
01987 //      METHOD NAME : RpmDb::removePackage
01988 //      METHOD TYPE : PMError
01989 //
01990 void RpmDb::removePackage( const string & name_r, unsigned flags )
01991 {
01992   callback::SendReport<RpmRemoveReport> report;
01993 
01994   report->start( name_r );
01995 
01996   try
01997   {
01998     doRemovePackage(name_r, flags, report);
01999   }
02000   catch (RpmException & excpt_r)
02001   {
02002     report->finish(excpt_r);
02003     ZYPP_RETHROW(excpt_r);
02004   }
02005   report->finish();
02006 }
02007 
02008 
02009 void RpmDb::doRemovePackage( const string & name_r, unsigned flags, callback::SendReport<RpmRemoveReport> & report )
02010 {
02011   FAILIFNOTINITIALIZED;
02012   CommitLog progresslog;
02013 
02014   MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
02015 
02016   // backup
02017   if ( _packagebackups )
02018   {
02019     // FIXME solve this status report somehow
02020     //      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
02021     if ( ! backupPackage( name_r ) )
02022     {
02023       ERR << "backup of " << name_r << " failed" << endl;
02024     }
02025     report->progress( 0 );
02026   }
02027   else
02028   {
02029     report->progress( 100 );
02030   }
02031 
02032   // run rpm
02033   RpmArgVec opts;
02034   opts.push_back("-e");
02035   opts.push_back("--allmatches");
02036 
02037   if (flags & RPMINST_NOSCRIPTS)
02038     opts.push_back("--noscripts");
02039   if (flags & RPMINST_NODEPS)
02040     opts.push_back("--nodeps");
02041   if (flags & RPMINST_JUSTDB)
02042     opts.push_back("--justdb");
02043   if (flags & RPMINST_TEST)
02044     opts.push_back ("--test");
02045   if (flags & RPMINST_FORCE)
02046   {
02047     WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
02048   }
02049 
02050   opts.push_back("--");
02051   opts.push_back(name_r.c_str());
02052 
02053   modifyDatabase(); // BEFORE run_rpm
02054   run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
02055 
02056   string line;
02057   string rpmmsg;
02058 
02059   // got no progress from command, so we fake it:
02060   // 5  - command started
02061   // 50 - command completed
02062   // 100 if no error
02063   report->progress( 5 );
02064   while (systemReadLine(line))
02065   {
02066     rpmmsg += line+'\n';
02067   }
02068   report->progress( 50 );
02069   int rpm_status = systemStatus();
02070 
02071   if ( rpm_status != 0 )
02072   {
02073     // %s = name of rpm package
02074     progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
02075     progresslog() << _("rpm output:") << endl << rpmmsg << endl;
02076     ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
02077   }
02078   else
02079   {
02080     progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
02081     if ( ! rpmmsg.empty() )
02082     {
02083       progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
02084     }
02085   }
02086 }
02087 
02089 //
02090 //
02091 //      METHOD NAME : RpmDb::backupPackage
02092 //      METHOD TYPE : bool
02093 //
02094 bool RpmDb::backupPackage( const Pathname & filename )
02095 {
02096   RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
02097   if ( ! h )
02098     return false;
02099 
02100   return backupPackage( h->tag_name() );
02101 }
02102 
02104 //
02105 //
02106 //      METHOD NAME : RpmDb::backupPackage
02107 //      METHOD TYPE : bool
02108 //
02109 bool RpmDb::backupPackage(const string& packageName)
02110 {
02111   CommitLog progresslog;
02112   bool ret = true;
02113   Pathname backupFilename;
02114   Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
02115 
02116   if (_backuppath.empty())
02117   {
02118     INT << "_backuppath empty" << endl;
02119     return false;
02120   }
02121 
02122   FileList fileList;
02123 
02124   if (!queryChangedFiles(fileList, packageName))
02125   {
02126     ERR << "Error while getting changed files for package " <<
02127     packageName << endl;
02128     return false;
02129   }
02130 
02131   if (fileList.size() <= 0)
02132   {
02133     DBG <<  "package " <<  packageName << " not changed -> no backup" << endl;
02134     return true;
02135   }
02136 
02137   if (filesystem::assert_dir(_root + _backuppath) != 0)
02138   {
02139     return false;
02140   }
02141 
02142   {
02143     // build up archive name
02144     time_t currentTime = time(0);
02145     struct tm *currentLocalTime = localtime(&currentTime);
02146 
02147     int date = (currentLocalTime->tm_year + 1900) * 10000
02148                + (currentLocalTime->tm_mon + 1) * 100
02149                + currentLocalTime->tm_mday;
02150 
02151     int num = 0;
02152     do
02153     {
02154       backupFilename = _root + _backuppath
02155                        + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
02156 
02157     }
02158     while ( PathInfo(backupFilename).isExist() && num++ < 1000);
02159 
02160     PathInfo pi(filestobackupfile);
02161     if (pi.isExist() && !pi.isFile())
02162     {
02163       ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
02164       return false;
02165     }
02166 
02167     ofstream fp ( filestobackupfile.asString().c_str(), ios::out|ios::trunc );
02168 
02169     if (!fp)
02170     {
02171       ERR << "could not open " << filestobackupfile.asString() << endl;
02172       return false;
02173     }
02174 
02175     for (FileList::const_iterator cit = fileList.begin();
02176          cit != fileList.end(); ++cit)
02177     {
02178       string name = *cit;
02179       if ( name[0] == '/' )
02180       {
02181         // remove slash, file must be relative to -C parameter of tar
02182         name = name.substr( 1 );
02183       }
02184       DBG << "saving file "<< name << endl;
02185       fp << name << endl;
02186     }
02187     fp.close();
02188 
02189     const char* const argv[] =
02190       {
02191         "tar",
02192         "-czhP",
02193         "-C",
02194         _root.asString().c_str(),
02195         "--ignore-failed-read",
02196         "-f",
02197         backupFilename.asString().c_str(),
02198         "-T",
02199         filestobackupfile.asString().c_str(),
02200         NULL
02201       };
02202 
02203     // execute tar in inst-sys (we dont know if there is a tar below _root !)
02204     ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
02205 
02206     string tarmsg;
02207 
02208     // TODO: its probably possible to start tar with -v and watch it adding
02209     // files to report progress
02210     for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
02211     {
02212       tarmsg+=output;
02213     }
02214 
02215     int ret = tar.close();
02216 
02217     if ( ret != 0)
02218     {
02219       ERR << "tar failed: " << tarmsg << endl;
02220       ret = false;
02221     }
02222     else
02223     {
02224       MIL << "tar backup ok" << endl;
02225       progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
02226     }
02227 
02228     filesystem::unlink(filestobackupfile);
02229   }
02230 
02231   return ret;
02232 }
02233 
02234 void RpmDb::setBackupPath(const Pathname& path)
02235 {
02236   _backuppath = path;
02237 }
02238 
02239 } // namespace rpm
02240 } // namespace target
02241 } // namespace zypp

Generated on Tue Sep 25 19:23:09 2007 for libzypp by  doxygen 1.5.3