RpmDb.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include "librpm.h"
00013 
00014 #include <cstdlib>
00015 #include <cstdio>
00016 #include <ctime>
00017 
00018 #include <iostream>
00019 #include <fstream>
00020 #include <list>
00021 #include <map>
00022 #include <set>
00023 #include <string>
00024 #include <vector>
00025 #include <algorithm>
00026 
00027 #include "zypp/base/Logger.h"
00028 #include "zypp/base/String.h"
00029 
00030 #include "zypp/Date.h"
00031 #include "zypp/Pathname.h"
00032 #include "zypp/PathInfo.h"
00033 #include "zypp/PublicKey.h"
00034 
00035 #include "zypp/target/rpm/RpmDb.h"
00036 #include "zypp/target/rpm/RpmCallbacks.h"
00037 
00038 #include "zypp/target/CommitLog.h"
00039 #include "zypp/target/rpm/librpmDb.h"
00040 #include "zypp/target/rpm/RpmPackageImpl.h"
00041 #include "zypp/target/rpm/RpmException.h"
00042 #include "zypp/CapSet.h"
00043 #include "zypp/CapFactory.h"
00044 #include "zypp/KeyRing.h"
00045 #include "zypp/ZYppFactory.h"
00046 #include "zypp/TmpPath.h"
00047 
00048 #ifndef _
00049 #define _(X) X
00050 #endif
00051 
00052 using namespace std;
00053 using namespace zypp::filesystem;
00054 
00055 namespace zypp
00056 {
00057   namespace target
00058   {
00059     namespace rpm
00060     {
00061       namespace
00062       {
00063         const char* quoteInFilename_m = " \t";
00064         inline std::string rpmQuoteFilename( const Pathname & path_r )
00065         {
00066           std::string path( path_r.asString() );
00067           for ( std::string::size_type pos = path.find_first_of( quoteInFilename_m );
00068                 pos != std::string::npos;
00069                 pos = path.find_first_of( quoteInFilename_m, pos ) )
00070             {
00071               path.insert( pos, "\\" );
00072               pos += 2; // skip '\\' and the quoted char.
00073             }
00074           return path;
00075         }
00076       }
00077 
00078       struct KeyRingSignalReceiver : callback::ReceiveReport<KeyRingSignals>
00079       {
00080         KeyRingSignalReceiver(RpmDb &rpmdb) : _rpmdb(rpmdb)
00081         {
00082           connect();
00083         }
00084 
00085         ~KeyRingSignalReceiver()
00086         {
00087           disconnect();
00088         }
00089 
00090         virtual void trustedKeyAdded( const KeyRing &, const PublicKey &key )
00091         {
00092           MIL << "Trusted key added to zypp Keyring. Importing" << endl;
00093           // now import the key in rpm
00094           try
00095           {
00096             _rpmdb.importPubkey( key );
00097           }
00098           catch (RpmException &e)
00099           {
00100             ERR << "Could not import key " << key.id() << " (" << key.name() << " from " << key.path() << " in rpm database" << endl;
00101           }
00102         }
00103 
00104         virtual void trustedKeyRemoved( const KeyRing &, const PublicKey &key  )
00105         {
00106           ERR << "Can't remove key " << key << " from rpm database." << endl;
00107         }
00108 
00109         RpmDb &_rpmdb;
00110       };
00111 
00112       static shared_ptr<KeyRingSignalReceiver> sKeyRingReceiver;
00113 
00114       unsigned diffFiles(const std::string file1, const std::string file2, std::string& out, int maxlines)
00115       {
00116         const char* argv[] =
00117         {
00118           "diff",
00119           "-u",
00120           file1.c_str(),
00121           file2.c_str(),
00122           NULL
00123         };
00124         ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00125 
00126         //if(!prog)
00127         //return 2;
00128 
00129         string line;
00130         int count = 0;
00131         for (line = prog.receiveLine(), count=0;
00132               !line.empty();
00133               line = prog.receiveLine(), count++ )
00134           {
00135             if (maxlines<0?true:count<maxlines)
00136               out+=line;
00137           }
00138 
00139         return prog.close();
00140       }
00141 
00142 
00143 
00144       /******************************************************************
00145        **
00146        **
00147        **       FUNCTION NAME : stringPath
00148        **       FUNCTION TYPE : inline string
00149       */
00150       inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
00151       {
00152         return librpmDb::stringPath( root_r, sub_r );
00153       }
00154 
00155       /******************************************************************
00156        **
00157        **
00158        **       FUNCTION NAME : operator<<
00159        **       FUNCTION TYPE : ostream &
00160       */
00161       ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
00162       {
00163         if ( obj == RpmDb::DbSI_NO_INIT )
00164           {
00165             str << "NO_INIT";
00166           }
00167         else
00168           {
00169 #define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
00170             str << "V4(";
00171             ENUM_OUT( DbSI_HAVE_V4,     'X' );
00172             ENUM_OUT( DbSI_MADE_V4,     'c' );
00173             ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
00174             str << ")V3(";
00175             ENUM_OUT( DbSI_HAVE_V3,     'X' );
00176             ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
00177             ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
00178             str << ")";
00179 #undef ENUM_OUT
00180           }
00181         return str;
00182       }
00183 
00185       //        CLASS NAME : RpmDbPtr
00186       //        CLASS NAME : RpmDbconstPtr
00188 
00189 #define WARNINGMAILPATH "/var/log/YaST2/"
00190 #define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
00191 
00193       //
00194       //        CLASS NAME : RpmDb::Packages
00213       class RpmDb::Packages
00214       {
00215       public:
00216         list<Package::Ptr>        _list;
00217         map<std::string,Package::Ptr> _index;
00218         bool                      _valid;
00219         Packages() : _valid( false )
00220         {}
00221         void clear()
00222         {
00223           _list.clear();
00224           _index.clear();
00225           _valid = false;
00226         }
00227         Package::Ptr lookup( const string & name_r ) const
00228         {
00229           map<string,Package::Ptr>::const_iterator got = _index.find( name_r );
00230           if ( got != _index.end() )
00231             return got->second;
00232           return Package::Ptr();
00233         }
00234         void buildIndex()
00235         {
00236           _index.clear();
00237           for ( list<Package::Ptr>::iterator iter = _list.begin();
00238                 iter != _list.end(); ++iter )
00239             {
00240               string name = (*iter)->name();
00241               Package::Ptr & nptr = _index[name]; // be shure to get a reference!
00242 
00243               if ( nptr )
00244                 {
00245                   WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
00246                   if ( nptr->installtime() > (*iter)->installtime() )
00247                     continue;
00248                   else
00249                     nptr = *iter;
00250                 }
00251               else
00252                 {
00253                   nptr = *iter;
00254                 }
00255             }
00256           _valid = true;
00257         }
00258       };
00259 
00261 
00263       //
00264       //        CLASS NAME : RpmDb
00265       //
00267 
00268 #define FAILIFNOTINITIALIZED if( ! initialized() ) { ZYPP_THROW(RpmDbNotOpenException()); }
00269 
00271 
00273       //
00274       //
00275       //        METHOD NAME : RpmDb::RpmDb
00276       //        METHOD TYPE : Constructor
00277       //
00278       RpmDb::RpmDb()
00279       : _dbStateInfo( DbSI_NO_INIT )
00280       , _packages( * new Packages ) // delete in destructor
00281 #warning Check for obsolete memebers
00282       , _backuppath ("/var/adm/backup")
00283       , _packagebackups(false)
00284       , _warndirexists(false)
00285       {
00286         process = 0;
00287         exit_code = -1;
00288 
00289         // Some rpm versions are patched not to abort installation if
00290         // symlink creation failed.
00291         setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
00292         sKeyRingReceiver.reset(new KeyRingSignalReceiver(*this));
00293       }
00294 
00296       //
00297       //
00298       //        METHOD NAME : RpmDb::~RpmDb
00299       //        METHOD TYPE : Destructor
00300       //
00301       RpmDb::~RpmDb()
00302       {
00303         MIL << "~RpmDb()" << endl;
00304         closeDatabase();
00305 
00306         delete process;
00307         delete &_packages;
00308         MIL  << "~RpmDb() end" << endl;
00309         sKeyRingReceiver.reset();
00310       }
00311 
00312       Date RpmDb::timestamp() const
00313       {
00314         Date ts_rpm;
00315 
00316         Pathname db_path;
00317         if ( dbPath().empty() )
00318           db_path = "/var/lib/rpm";
00319         else
00320           db_path = dbPath();
00321 
00322         PathInfo rpmdb_info(root() + db_path + "/Packages");
00323 
00324         if ( rpmdb_info.isExist() )
00325           return rpmdb_info.mtime();
00326         else
00327           return Date::now();
00328       }
00330       //
00331       //
00332       //        METHOD NAME : RpmDb::dumpOn
00333       //        METHOD TYPE : std::ostream &
00334       //
00335       std::ostream & RpmDb::dumpOn( std::ostream & str ) const
00336       {
00337         str << "RpmDb[";
00338 
00339         if ( _dbStateInfo == DbSI_NO_INIT )
00340           {
00341             str << "NO_INIT";
00342           }
00343         else
00344           {
00345 #define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
00346             str << "V4(";
00347             ENUM_OUT( DbSI_HAVE_V4,     'X' );
00348             ENUM_OUT( DbSI_MADE_V4,     'c' );
00349             ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
00350             str << ")V3(";
00351             ENUM_OUT( DbSI_HAVE_V3,     'X' );
00352             ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
00353             ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
00354             str << "): " << stringPath( _root, _dbPath );
00355 #undef ENUM_OUT
00356           }
00357         return str << "]";
00358       }
00359 
00361       //
00362       //
00363       //        METHOD NAME : RpmDb::initDatabase
00364       //        METHOD TYPE : PMError
00365       //
00366       void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
00367       {
00369         // Check arguments
00371         if ( root_r.empty() )
00372           root_r = "/";
00373 
00374         if ( dbPath_r.empty() )
00375           dbPath_r = "/var/lib/rpm";
00376 
00377         if ( ! (root_r.absolute() && dbPath_r.absolute()) )
00378           {
00379             ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
00380             ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
00381           }
00382 
00383         MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
00384 
00386         // Check whether already initialized
00388         if ( initialized() )
00389           {
00390             if ( root_r == _root && dbPath_r == _dbPath )
00391               {
00392                 return;
00393               }
00394             else
00395               {
00396                 ZYPP_THROW(RpmDbAlreadyOpenException(_root, _dbPath, root_r, dbPath_r));
00397               }
00398           }
00399 
00401         // init database
00403         librpmDb::unblockAccess();
00404         DbStateInfoBits info = DbSI_NO_INIT;
00405         try
00406           {
00407             internal_initDatabase( root_r, dbPath_r, info );
00408           }
00409         catch (const RpmException & excpt_r)
00410           {
00411             ZYPP_CAUGHT(excpt_r);
00412             librpmDb::blockAccess();
00413             ERR << "Cleanup on error: state " << info << endl;
00414 
00415             if ( dbsi_has( info, DbSI_MADE_V4 ) )
00416               {
00417                 // remove the newly created rpm4 database and
00418                 // any backup created on conversion.
00419                 removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
00420               }
00421             ZYPP_RETHROW(excpt_r);
00422           }
00423         if ( dbsi_has( info, DbSI_HAVE_V3 ) )
00424           {
00425             if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) )
00426               {
00427                 // Move obsolete rpm3 database beside.
00428                 MIL << "Cleanup: state " << info << endl;
00429                 removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
00430                 dbsi_clr( info, DbSI_HAVE_V3 );
00431               }
00432             else
00433               {
00434                 // Performing an update: Keep the original rpm3 database
00435                 // and wait if the rpm4 database gets modified by installing
00436                 // or removing packages. Cleanup in modifyDatabase or closeDatabase.
00437                 MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
00438               }
00439           }
00440 #warning CHECK: notify root about conversion backup.
00441 
00442         _root   = root_r;
00443         _dbPath = dbPath_r;
00444         _dbStateInfo = info;
00445 
00446 #warning Add rebuild database once have the info about context
00447 #if 0
00448         if ( ! ( Y2PM::runningFromSystem() ) )
00449           {
00450             if (      dbsi_has( info, DbSI_HAVE_V4 )
00451                  && ! dbsi_has( info, DbSI_MADE_V4 ) )
00452               {
00453                 err = rebuildDatabase();
00454               }
00455           }
00456 #endif
00457 
00458         MIL << "Syncronizing keys with zypp keyring" << std::endl;
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::exportTrustedKeysInZyppKeyRing()
00890       {
00891         MIL << "Exporting rpm keyring into zypp trusted keyring" <<std::endl;
00892 
00893         std::set<Edition> rpm_keys = pubkeyEditions();
00894 
00895         std::list<PublicKey> zypp_keys;
00896         zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
00897 
00898         for ( std::set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
00899           {
00900             // search the zypp key into the rpm keys
00901             // long id is edition version + release
00902             std::string id = str::toUpper( (*it).version() + (*it).release());
00903             std::list<PublicKey>::iterator ik = find( zypp_keys.begin(), zypp_keys.end(), id);
00904             if ( ik != zypp_keys.end() )
00905               {
00906                 MIL << "Key " << (*it) << " is already in zypp database." << std::endl;
00907               }
00908             else
00909               {
00910                 // we export the rpm key into a file
00911                 RpmHeader::constPtr result = new RpmHeader();
00912                 getData( std::string("gpg-pubkey"), *it, result );
00913                 TmpFile file(getZYpp()->tmpPath());
00914                 std::ofstream os;
00915                 try
00916                   {
00917                     os.open(file.path().asString().c_str());
00918                     // dump rpm key into the tmp file
00919                     os << result->tag_description();
00920                     //MIL << "-----------------------------------------------" << std::endl;
00921                     //MIL << result->tag_description() <<std::endl;
00922                     //MIL << "-----------------------------------------------" << std::endl;
00923                     os.close();
00924                   }
00925                 catch (std::exception &e)
00926                   {
00927                     ERR << "Could not dump key " << (*it) << " in tmp file " << file.path() << std::endl;
00928                     // just ignore the key
00929                   }
00930 
00931                 // now import the key in zypp
00932                 try
00933                   {
00934                     getZYpp()->keyRing()->importKey( file.path(), true /*trusted*/);
00935                     MIL << "Trusted key " << (*it) << " imported in zypp keyring." << std::endl;
00936                   }
00937                 catch (Exception &e)
00938                   {
00939                     ERR << "Could not import key " << (*it) << " in zypp keyring" << std::endl;
00940                   }
00941               }
00942           }
00943       }
00944 
00945       void RpmDb::importZyppKeyRingTrustedKeys()
00946       {
00947         MIL << "Importing zypp trusted keyring" << std::endl;
00948 
00949         std::list<PublicKey> rpm_keys = pubkeys();
00950 
00951         std::list<PublicKey> zypp_keys;
00952 
00953         zypp_keys = getZYpp()->keyRing()->trustedPublicKeys();
00954 
00955         for ( std::list<PublicKey>::const_iterator it = zypp_keys.begin(); it != zypp_keys.end(); ++it)
00956           {
00957             // we find only the left part of the long gpg key, as rpm does not support long ids
00958             std::list<PublicKey>::iterator ik = find( rpm_keys.begin(), rpm_keys.end(), (*it));
00959             if ( ik != rpm_keys.end() )
00960               {
00961                 MIL << "Key " << (*it).id() << " (" << (*it).name() << ") is already in rpm database." << std::endl;
00962               }
00963             else
00964               {
00965                 // now import the key in rpm
00966                 try
00967                   {
00968                     importPubkey(*it);
00969                     MIL << "Trusted key " << (*it).id() << " (" << (*it).name() << ") imported in rpm database." << std::endl;
00970                   }
00971                 catch (RpmException &e)
00972                   {
00973                     ERR << "Could not import key " << (*it).id() << " (" << (*it).name() << " from " << (*it).path() << " in rpm database" << std::endl;
00974                   }
00975               }
00976           }
00977       }
00978 
00980       //
00981       //
00982       //        METHOD NAME : RpmDb::importPubkey
00983       //        METHOD TYPE : PMError
00984       //
00985       void RpmDb::importPubkey( const Pathname & pubkey_r )
00986       { importPubkey( PublicKey(pubkey_r) ); }
00987 
00988       void RpmDb::importPubkey( const PublicKey & pubkey_r )
00989       {
00990         FAILIFNOTINITIALIZED;
00991 
00992         // check if the key is already in the rpm database and just
00993         // return if it does.
00994         set<Edition> rpm_keys = pubkeyEditions();
00995         string keyshortid = pubkey_r.id().substr(8,8);
00996         MIL << "Comparing '" << keyshortid << "' to: ";
00997         for ( set<Edition>::const_iterator it = rpm_keys.begin(); it != rpm_keys.end(); ++it)
00998         {
00999           string id = str::toUpper( (*it).version() );
01000           MIL <<  ", '" << id << "'";
01001           if ( id == keyshortid )
01002           {
01003               // they match id
01004               // now check if timestamp is different
01005               Date date = Date(str::strtonum<Date::ValueType>("0x" + (*it).release()));
01006               if (  date == pubkey_r.created() )
01007               {
01008 
01009                   MIL << endl << "Key " << pubkey_r << " is already in the rpm trusted keyring." << endl;
01010                   return;
01011               }
01012               else
01013               {
01014                   MIL << endl << "Key " << pubkey_r << " has another version in keyring. ( " << date << " & " << pubkey_r.created() << ")" << endl;
01015 
01016               }
01017 
01018           }
01019         }
01020         // key does not exists, lets import it
01021         MIL <<  endl;
01022 
01023         RpmArgVec opts;
01024         opts.push_back ( "--import" );
01025         opts.push_back ( "--" );
01026         opts.push_back ( pubkey_r.path().asString().c_str() );
01027 
01028         // don't call modifyDatabase because it would remove the old
01029         // rpm3 database, if the current database is a temporary one.
01030         // But do invalidate packages list.
01031         _packages._valid = false;
01032         run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01033 
01034         string line;
01035         while ( systemReadLine( line ) )
01036           {
01037             if ( line.substr( 0, 6 ) == "error:" )
01038               {
01039                 WAR << line << endl;
01040               }
01041             else
01042               {
01043                 DBG << line << endl;
01044               }
01045           }
01046 
01047         int rpm_status = systemStatus();
01048 
01049         if ( rpm_status != 0 )
01050           {
01051             ZYPP_THROW(RpmSubprocessException(string("Failed to import public key from file ") + pubkey_r.path().asString() + string(": rpm returned  ") + str::numstring(rpm_status)));
01052           }
01053         else
01054           {
01055             MIL << "Imported public key from file " << pubkey_r.path() << endl;
01056           }
01057       }
01058 
01060       //
01061       //
01062       //        METHOD NAME : RpmDb::pubkeys
01063       //        METHOD TYPE : set<Edition>
01064       //
01065       list<PublicKey> RpmDb::pubkeys() const
01066       {
01067         list<PublicKey> ret;
01068 
01069         librpmDb::db_const_iterator it;
01070         for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
01071           {
01072             Edition edition = it->tag_edition();
01073             if (edition != Edition::noedition)
01074               {
01075                 // we export the rpm key into a file
01076                 RpmHeader::constPtr result = new RpmHeader();
01077                 getData( std::string("gpg-pubkey"), edition, result );
01078                 TmpFile file(getZYpp()->tmpPath());
01079                 std::ofstream os;
01080                 try
01081                   {
01082                     os.open(file.path().asString().c_str());
01083                     // dump rpm key into the tmp file
01084                     os << result->tag_description();
01085                     //MIL << "-----------------------------------------------" << std::endl;
01086                     //MIL << result->tag_description() <<std::endl;
01087                     //MIL << "-----------------------------------------------" << std::endl;
01088                     os.close();
01089                     // read the public key from the dumped file
01090                     PublicKey key(file.path());
01091                     ret.push_back(key);
01092                   }
01093                 catch (std::exception &e)
01094                   {
01095                     ERR << "Could not dump key " << edition.asString() << " in tmp file " << file.path() << std::endl;
01096                     // just ignore the key
01097                   }
01098               }
01099           }
01100         return ret;
01101       }
01102 
01103       set<Edition> RpmDb::pubkeyEditions() const
01104       {
01105         set<Edition> ret;
01106 
01107         librpmDb::db_const_iterator it;
01108         for ( it.findByName( string( "gpg-pubkey" ) ); *it; ++it )
01109           {
01110             Edition edition = it->tag_edition();
01111             if (edition != Edition::noedition)
01112               ret.insert( edition );
01113           }
01114         return ret;
01115       }
01116 
01118       //
01119       //
01120       //        METHOD NAME : RpmDb::packagesValid
01121       //        METHOD TYPE : bool
01122       //
01123       bool RpmDb::packagesValid() const
01124       {
01125         return( _packages._valid || ! initialized() );
01126       }
01127 
01129       //
01130       //
01131       //        METHOD NAME : RpmDb::getPackages
01132       //        METHOD TYPE : const std::list<Package::Ptr> &
01133       //
01134       //        DESCRIPTION :
01135       //
01136       const std::list<Package::Ptr> & RpmDb::getPackages()
01137       {
01138         callback::SendReport<ScanDBReport> report;
01139 
01140         report->start ();
01141 
01142         try
01143           {
01144             const std::list<Package::Ptr> & ret = doGetPackages(report);
01145             report->finish(ScanDBReport::NO_ERROR, "");
01146             return ret;
01147           }
01148         catch (RpmException & excpt_r)
01149           {
01150             report->finish(ScanDBReport::FAILED, excpt_r.asUserString ());
01151             ZYPP_RETHROW(excpt_r);
01152           }
01153 #warning fixme
01154         static const std::list<Package::Ptr> empty_list;
01155         return empty_list;
01156       }
01157 
01158 
01159       //
01160       // make Package::Ptr from RpmHeader
01161       // return NULL on error
01162       //
01163       Package::Ptr RpmDb::makePackageFromHeader( const RpmHeader::constPtr header, std::set<std::string> * filerequires, const Pathname & location, Source_Ref source )
01164       {
01165         if ( ! header )
01166           return 0;
01167 
01168         if ( header->isSrc() )
01169           {
01170             WAR << "Can't make Package from SourcePackage header" << endl;
01171             return 0;
01172           }
01173 
01174         Package::Ptr pptr;
01175 
01176         string name = header->tag_name();
01177 
01178         // create dataprovider
01179         detail::ResImplTraits<RPMPackageImpl>::Ptr impl( new RPMPackageImpl( header ) );
01180 
01181         impl->setSource( source );
01182         if (!location.empty())
01183           impl->setLocation( location );
01184 
01185         Edition edition;
01186         try
01187           {
01188             edition = Edition( header->tag_version(),
01189                                header->tag_release(),
01190                                header->tag_epoch());
01191           }
01192         catch (Exception & excpt_r)
01193           {
01194             ZYPP_CAUGHT( excpt_r );
01195             WAR << "Package " << name << " has bad edition '"
01196             << (header->tag_epoch().empty()?"":(header->tag_epoch()+":"))
01197             << header->tag_version()
01198             << (header->tag_release().empty()?"":(string("-") + header->tag_release())) << "'";
01199             return pptr;
01200           }
01201 
01202         Arch arch;
01203         try
01204           {
01205             arch = Arch( header->tag_arch() );
01206           }
01207         catch (Exception & excpt_r)
01208           {
01209             ZYPP_CAUGHT( excpt_r );
01210             WAR << "Package " << name << " has bad architecture '" << header->tag_arch() << "'";
01211             return pptr;
01212           }
01213 
01214         // Collect basic Resolvable data
01215         NVRAD dataCollect( header->tag_name(),
01216                            edition,
01217                            arch );
01218 
01219         list<string> filenames = impl->filenames();
01220         dataCollect[Dep::PROVIDES] = header->tag_provides ( filerequires );
01221         CapFactory capfactory;
01222 
01223         static str::smatch what;
01224         static const str::regex filenameRegex( "/(s?bin|lib(64)?|etc)/|^/usr/(games/|share/(dict/words|magic\\.mime)$)|^/opt/gnome/games/",
01225                                                str::regex::optimize|str::regex::nosubs );
01226 
01227         for (list<string>::const_iterator filename = filenames.begin();
01228               filename != filenames.end();
01229               ++filename)
01230           {
01231             if ( str::regex_search( filename->begin(), filename->end(), what, filenameRegex ) )
01232               {
01233                 try
01234                   {
01235                     dataCollect[Dep::PROVIDES].insert( capfactory.parse(ResTraits<Package>::kind, *filename) );
01236                   }
01237                 catch (Exception & excpt_r)
01238                   {
01239                     ZYPP_CAUGHT( excpt_r );
01240                     WAR << "Ignoring invalid capability: " << *filename << endl;
01241                   }
01242               }
01243           }
01244 
01245         dataCollect[Dep::REQUIRES]    = header->tag_requires( filerequires );
01246         dataCollect[Dep::PREREQUIRES] = header->tag_prerequires( filerequires );
01247         dataCollect[Dep::CONFLICTS]   = header->tag_conflicts( filerequires );
01248         dataCollect[Dep::OBSOLETES]   = header->tag_obsoletes( filerequires );
01249         dataCollect[Dep::ENHANCES]    = header->tag_enhances( filerequires );
01250         dataCollect[Dep::SUPPLEMENTS] = header->tag_supplements( filerequires );
01251 
01252         try
01253           {
01254             // create package from dataprovider
01255             pptr = detail::makeResolvableFromImpl( dataCollect, impl );
01256           }
01257         catch (Exception & excpt_r)
01258           {
01259             ZYPP_CAUGHT( excpt_r );
01260             ERR << "Can't create Package::Ptr" << endl;
01261           }
01262 
01263         return pptr;
01264       }
01265 
01266 
01267       const std::list<Package::Ptr> & RpmDb::doGetPackages(callback::SendReport<ScanDBReport> & report)
01268       {
01269         if ( packagesValid() )
01270           {
01271             return _packages._list;
01272           }
01273 
01274         _packages.clear();
01275 
01277         // Collect package data.
01279         unsigned expect = 0;
01280         librpmDb::constPtr dbptr;
01281         librpmDb::dbAccess( dbptr );
01282         expect = dbptr->size();
01283         DBG << "Expecting " << expect << " packages" << endl;
01284 
01285         librpmDb::db_const_iterator iter;
01286         unsigned current = 0;
01287         CapFactory _f;
01288         Pathname location;
01289 
01290         for ( iter.findAll(); *iter; ++iter, ++current, report->progress( (100*current)/expect))
01291           {
01292 
01293             string name = iter->tag_name();
01294             if ( name == string( "gpg-pubkey" ) )
01295               {
01296                 DBG << "Ignoring pseudo package " << name << endl;
01297                 // pseudo package filtered, as we can't handle multiple instances
01298                 // of 'gpg-pubkey-VERS-REL'.
01299                 continue;
01300               }
01301 
01302             Package::Ptr pptr = makePackageFromHeader( *iter, &_filerequires, location, Source_Ref() );
01303             if ( ! pptr )
01304               {
01305                 WAR << "Failed to make package from database header '" << name << "'" << endl;
01306                 continue;
01307               }
01308 
01309             _packages._list.push_back( pptr );
01310           }
01311         _packages.buildIndex();
01312         DBG << "Found installed packages: " << _packages._list.size() << endl;
01313 
01315         // Evaluate filerequires collected so far
01317         for ( set<string>::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it )
01318           {
01319 
01320             for ( iter.findByFile( *it ); *iter; ++iter )
01321               {
01322                 Package::Ptr pptr = _packages.lookup( iter->tag_name() );
01323                 if ( !pptr )
01324                   {
01325                     WAR << "rpmdb.findByFile returned unknown package " << *iter << endl;
01326                     continue;
01327                   }
01328                 pptr->injectProvides(_f.parse(ResTraits<Package>::kind, *it));
01329               }
01330 
01331           }
01332 
01334         // Build final packages list
01336         return _packages._list;
01337       }
01338 
01340       //
01341       //
01342       //        METHOD NAME : RpmDb::fileList
01343       //        METHOD TYPE : bool
01344       //
01345       //        DESCRIPTION :
01346       //
01347       std::list<FileInfo>
01348       RpmDb::fileList( const std::string & name_r, const Edition & edition_r ) const
01349       {
01350         std::list<FileInfo> result;
01351 
01352         librpmDb::db_const_iterator it;
01353         bool found;
01354         if (edition_r == Edition::noedition)
01355           {
01356             found = it.findPackage( name_r );
01357           }
01358         else
01359           {
01360             found = it.findPackage( name_r, edition_r );
01361           }
01362         if (!found)
01363           return result;
01364 
01365         return result;
01366       }
01367 
01368 
01370       //
01371       //
01372       //        METHOD NAME : RpmDb::hasFile
01373       //        METHOD TYPE : bool
01374       //
01375       //        DESCRIPTION :
01376       //
01377       bool RpmDb::hasFile( const std::string & file_r, const std::string & name_r ) const
01378       {
01379         librpmDb::db_const_iterator it;
01380         bool res;
01381         do
01382         {
01383           res = it.findByFile( file_r );
01384           if (!res) break;
01385           if (!name_r.empty())
01386             {
01387               res = (it->tag_name() == name_r);
01388             }
01389           ++it;
01390         }
01391         while (res && *it);
01392         return res;
01393       }
01394 
01396       //
01397       //
01398       //        METHOD NAME : RpmDb::whoOwnsFile
01399       //        METHOD TYPE : string
01400       //
01401       //        DESCRIPTION :
01402       //
01403       std::string RpmDb::whoOwnsFile( const std::string & file_r) const
01404       {
01405         librpmDb::db_const_iterator it;
01406         if (it.findByFile( file_r ))
01407           {
01408             return it->tag_name();
01409           }
01410         return "";
01411       }
01412 
01414       //
01415       //
01416       //        METHOD NAME : RpmDb::hasProvides
01417       //        METHOD TYPE : bool
01418       //
01419       //        DESCRIPTION :
01420       //
01421       bool RpmDb::hasProvides( const std::string & tag_r ) const
01422       {
01423         librpmDb::db_const_iterator it;
01424         return it.findByProvides( tag_r );
01425       }
01426 
01428       //
01429       //
01430       //        METHOD NAME : RpmDb::hasRequiredBy
01431       //        METHOD TYPE : bool
01432       //
01433       //        DESCRIPTION :
01434       //
01435       bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
01436       {
01437         librpmDb::db_const_iterator it;
01438         return it.findByRequiredBy( tag_r );
01439       }
01440 
01442       //
01443       //
01444       //        METHOD NAME : RpmDb::hasConflicts
01445       //        METHOD TYPE : bool
01446       //
01447       //        DESCRIPTION :
01448       //
01449       bool RpmDb::hasConflicts( const std::string & tag_r ) const
01450       {
01451         librpmDb::db_const_iterator it;
01452         return it.findByConflicts( tag_r );
01453       }
01454 
01456       //
01457       //
01458       //        METHOD NAME : RpmDb::hasPackage
01459       //        METHOD TYPE : bool
01460       //
01461       //        DESCRIPTION :
01462       //
01463       bool RpmDb::hasPackage( const string & name_r ) const
01464       {
01465         librpmDb::db_const_iterator it;
01466         return it.findPackage( name_r );
01467       }
01468 
01470       //
01471       //
01472       //        METHOD NAME : RpmDb::hasPackage
01473       //        METHOD TYPE : bool
01474       //
01475       //        DESCRIPTION :
01476       //
01477       bool RpmDb::hasPackage( const string & name_r, const Edition & ed_r ) const
01478       {
01479         librpmDb::db_const_iterator it;
01480         return it.findPackage( name_r, ed_r );
01481       }
01482 
01484       //
01485       //
01486       //        METHOD NAME : RpmDb::getData
01487       //        METHOD TYPE : PMError
01488       //
01489       //        DESCRIPTION :
01490       //
01491       void RpmDb::getData( const string & name_r,
01492                            RpmHeader::constPtr & result_r ) const
01493       {
01494         librpmDb::db_const_iterator it;
01495         it.findPackage( name_r );
01496         result_r = *it;
01497         if (it.dbError())
01498           ZYPP_THROW(*(it.dbError()));
01499       }
01500 
01502       //
01503       //
01504       //        METHOD NAME : RpmDb::getData
01505       //        METHOD TYPE : void
01506       //
01507       //        DESCRIPTION :
01508       //
01509       void RpmDb::getData( const std::string & name_r, const Edition & ed_r,
01510                            RpmHeader::constPtr & result_r ) const
01511       {
01512         librpmDb::db_const_iterator it;
01513         it.findPackage( name_r, ed_r  );
01514         result_r = *it;
01515         if (it.dbError())
01516           ZYPP_THROW(*(it.dbError()));
01517       }
01518 
01520       //
01521       //        METHOD NAME : RpmDb::checkPackage
01522       //        METHOD TYPE : RpmDb::checkPackageResult
01523       //
01524       RpmDb::checkPackageResult RpmDb::checkPackage( const Pathname & path_r )
01525       {
01526         PathInfo file( path_r );
01527         if ( ! file.isFile() ) {
01528           ERR << "Not a file: " << file << endl;
01529           return CHK_ERROR;
01530         }
01531 
01532         FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
01533         if ( fd == 0 || ::Ferror(fd) ) {
01534           ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
01535           if ( fd )
01536             ::Fclose( fd );
01537           return CHK_ERROR;
01538         }
01539 
01540         rpmts ts = ::rpmtsCreate();
01541         ::rpmtsSetRootDir( ts, root().asString().c_str() );
01542         ::rpmtsSetVSFlags( ts, RPMVSF_DEFAULT );
01543         int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), NULL );
01544         ts = ::rpmtsFree(ts);
01545 
01546         ::Fclose( fd );
01547 
01548         switch ( res )
01549           {
01550           case RPMRC_OK:
01551             return CHK_OK;
01552             break;
01553           case RPMRC_NOTFOUND:
01554             WAR << "Signature is unknown type. " << file << endl;
01555             return CHK_NOTFOUND;
01556             break;
01557           case RPMRC_FAIL:
01558             WAR << "Signature does not verify. " << file << endl;
01559             return CHK_FAIL;
01560             break;
01561           case RPMRC_NOTTRUSTED:
01562             WAR << "Signature is OK, but key is not trusted. " << file << endl;
01563             return CHK_NOTTRUSTED;
01564             break;
01565           case RPMRC_NOKEY:
01566             WAR << "Public key is unavailable. " << file << endl;
01567             return CHK_NOKEY;
01568             break;
01569           }
01570         ERR << "Error reading header." << file << endl;
01571         return CHK_ERROR;
01572       }
01573 
01574       // determine changed files of installed package
01575       bool
01576       RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
01577       {
01578         bool ok = true;
01579 
01580         fileList.clear();
01581 
01582         if ( ! initialized() ) return false;
01583 
01584         RpmArgVec opts;
01585 
01586         opts.push_back ("-V");
01587         opts.push_back ("--nodeps");
01588         opts.push_back ("--noscripts");
01589         opts.push_back ("--nomd5");
01590         opts.push_back ("--");
01591         opts.push_back (packageName.c_str());
01592 
01593         run_rpm (opts, ExternalProgram::Discard_Stderr);
01594 
01595         if ( process == NULL )
01596           return false;
01597 
01598         /* from rpm manpage
01599          5      MD5 sum
01600          S      File size
01601          L      Symlink
01602          T      Mtime
01603          D      Device
01604          U      User
01605          G      Group
01606          M      Mode (includes permissions and file type)
01607         */
01608 
01609         string line;
01610         while (systemReadLine(line))
01611           {
01612             if (line.length() > 12 &&
01613                  (line[0] == 'S' || line[0] == 's' ||
01614                    (line[0] == '.' && line[7] == 'T')))
01615               {
01616                 // file has been changed
01617                 string filename;
01618 
01619                 filename.assign(line, 11, line.length() - 11);
01620                 fileList.insert(filename);
01621               }
01622           }
01623 
01624         systemStatus();
01625         // exit code ignored, rpm returns 1 no matter if package is installed or
01626         // not
01627 
01628         return ok;
01629       }
01630 
01631 
01632 
01633       /****************************************************************/
01634       /* private member-functions                                       */
01635       /****************************************************************/
01636 
01637       /*--------------------------------------------------------------*/
01638       /* Run rpm with the specified arguments, handling stderr  */
01639       /* as specified  by disp                                  */
01640       /*--------------------------------------------------------------*/
01641       void
01642       RpmDb::run_rpm (const RpmArgVec& opts,
01643                        ExternalProgram::Stderr_Disposition disp)
01644       {
01645         if ( process )
01646           {
01647             delete process;
01648             process = NULL;
01649           }
01650         exit_code = -1;
01651 
01652         if ( ! initialized() )
01653           {
01654             ZYPP_THROW(RpmDbNotOpenException());
01655           }
01656 
01657         RpmArgVec args;
01658 
01659         // always set root and dbpath
01660         args.push_back("rpm");
01661         args.push_back("--root");
01662         args.push_back(_root.asString().c_str());
01663         args.push_back("--dbpath");
01664         args.push_back(_dbPath.asString().c_str());
01665 
01666         const char* argv[args.size() + opts.size() + 1];
01667 
01668         const char** p = argv;
01669         p = copy (args.begin (), args.end (), p);
01670         p = copy (opts.begin (), opts.end (), p);
01671         *p = 0;
01672 
01673         // Invalidate all outstanding database handles in case
01674         // the database gets modified.
01675         librpmDb::dbRelease( true );
01676 
01677         // Launch the program with default locale
01678         process = new ExternalProgram(argv, disp, false, -1, true);
01679         return;
01680       }
01681 
01682       /*--------------------------------------------------------------*/
01683       /* Read a line from the rpm process                               */
01684       /*--------------------------------------------------------------*/
01685       bool
01686       RpmDb::systemReadLine(string &line)
01687       {
01688         line.erase();
01689 
01690         if ( process == NULL )
01691           return false;
01692 
01693         line = process->receiveLine();
01694 
01695         if (line.length() == 0)
01696           return false;
01697 
01698         if (line[line.length() - 1] == '\n')
01699           line.erase(line.length() - 1);
01700 
01701         return true;
01702       }
01703 
01704       /*--------------------------------------------------------------*/
01705       /* Return the exit status of the rpm process, closing the */
01706       /* connection if not already done                         */
01707       /*--------------------------------------------------------------*/
01708       int
01709       RpmDb::systemStatus()
01710       {
01711         if ( process == NULL )
01712           return -1;
01713 
01714         exit_code = process->close();
01715         process->kill();
01716         delete process;
01717         process = 0;
01718 
01719         //   DBG << "exit code " << exit_code << endl;
01720 
01721         return exit_code;
01722       }
01723 
01724       /*--------------------------------------------------------------*/
01725       /* Forcably kill the rpm process                          */
01726       /*--------------------------------------------------------------*/
01727       void
01728       RpmDb::systemKill()
01729       {
01730         if (process) process->kill();
01731       }
01732 
01733 
01734       // generate diff mails for config files
01735       void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
01736       {
01737         string msg = line.substr(9);
01738         string::size_type pos1 = string::npos;
01739         string::size_type pos2 = string::npos;
01740         string file1s, file2s;
01741         Pathname file1;
01742         Pathname file2;
01743 
01744         pos1 = msg.find (typemsg);
01745         for (;;)
01746           {
01747             if ( pos1 == string::npos )
01748               break;
01749 
01750             pos2 = pos1 + strlen (typemsg);
01751 
01752             if (pos2 >= msg.length() )
01753               break;
01754 
01755             file1 = msg.substr (0, pos1);
01756             file2 = msg.substr (pos2);
01757 
01758             file1s = file1.asString();
01759             file2s = file2.asString();
01760 
01761             if (!_root.empty() && _root != "/")
01762               {
01763                 file1 = _root + file1;
01764                 file2 = _root + file2;
01765               }
01766 
01767             string out;
01768             int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
01769             if (ret)
01770               {
01771                 Pathname file = _root + WARNINGMAILPATH;
01772                 if (filesystem::assert_dir(file) != 0)
01773                   {
01774                     ERR << "Could not create " << file.asString() << endl;
01775                     break;
01776                   }
01777                 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
01778                 ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
01779                 if (!notify)
01780                   {
01781                     ERR << "Could not open " <<  file << endl;
01782                     break;
01783                   }
01784 
01785                 // Translator: %s = name of an rpm package. A list of diffs follows
01786                 // this message.
01787                 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
01788                 if (ret>1)
01789                   {
01790                     ERR << "diff failed" << endl;
01791                     notify << str::form(difffailmsg,
01792                                          file1s.c_str(), file2s.c_str()) << endl;
01793                   }
01794                 else
01795                   {
01796                     notify << str::form(diffgenmsg,
01797                                          file1s.c_str(), file2s.c_str()) << endl;
01798 
01799                     // remove root for the viewer's pleasure (#38240)
01800                     if (!_root.empty() && _root != "/")
01801                       {
01802                         if (out.substr(0,4) == "--- ")
01803                           {
01804                             out.replace(4, file1.asString().length(), file1s);
01805                           }
01806                         string::size_type pos = out.find("\n+++ ");
01807                         if (pos != string::npos)
01808                           {
01809                             out.replace(pos+5, file2.asString().length(), file2s);
01810                           }
01811                       }
01812                     notify << out << endl;
01813                   }
01814                 notify.close();
01815                 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
01816                 notify.close();
01817               }
01818             else
01819               {
01820                 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
01821               }
01822             break;
01823           }
01824       }
01825 
01827       //
01828       //
01829       //        METHOD NAME : RpmDb::installPackage
01830       //        METHOD TYPE : PMError
01831       //
01832       void RpmDb::installPackage( const Pathname & filename, unsigned flags )
01833       {
01834         callback::SendReport<RpmInstallReport> report;
01835 
01836         report->start(filename);
01837 
01838         do
01839         try
01840           {
01841             doInstallPackage(filename, flags, report);
01842             report->finish();
01843             break;
01844           }
01845         catch (RpmException & excpt_r)
01846           {
01847             RpmInstallReport::Action user = report->problem( excpt_r );
01848 
01849             if ( user == RpmInstallReport::ABORT )
01850               {
01851                 report->finish( excpt_r );
01852                 ZYPP_RETHROW(excpt_r);
01853               }
01854             else if ( user == RpmInstallReport::IGNORE )
01855               {
01856                 break;
01857               }
01858           }
01859         while (true);
01860       }
01861 
01862       void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callback::SendReport<RpmInstallReport> & report )
01863       {
01864         FAILIFNOTINITIALIZED;
01865         CommitLog progresslog;
01866 
01867         MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
01868 
01869 
01870         // backup
01871         if ( _packagebackups )
01872           {
01873             // FIXME      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
01874             if ( ! backupPackage( filename ) )
01875               {
01876                 ERR << "backup of " << filename.asString() << " failed" << endl;
01877               }
01878             // FIXME status handling
01879             report->progress( 0 ); // allow 1% for backup creation.
01880           }
01881         else
01882           {
01883             report->progress( 100 );
01884           }
01885 
01886         // run rpm
01887         RpmArgVec opts;
01888         if (flags & RPMINST_NOUPGRADE)
01889           opts.push_back("-i");
01890         else
01891           opts.push_back("-U");
01892         opts.push_back("--percent");
01893 
01894         if (flags & RPMINST_NODIGEST)
01895           opts.push_back("--nodigest");
01896         if (flags & RPMINST_NOSIGNATURE)
01897           opts.push_back("--nosignature");
01898         if (flags & RPMINST_NODOCS)
01899           opts.push_back ("--excludedocs");
01900         if (flags & RPMINST_NOSCRIPTS)
01901           opts.push_back ("--noscripts");
01902         if (flags & RPMINST_FORCE)
01903           opts.push_back ("--force");
01904         if (flags & RPMINST_NODEPS)
01905           opts.push_back ("--nodeps");
01906         if (flags & RPMINST_IGNORESIZE)
01907           opts.push_back ("--ignoresize");
01908         if (flags & RPMINST_JUSTDB)
01909           opts.push_back ("--justdb");
01910         if (flags & RPMINST_TEST)
01911           opts.push_back ("--test");
01912 
01913         opts.push_back("--");
01914 
01915         // rpm requires additional quoting of special chars:
01916         std::string quotedFilename( rpmQuoteFilename( filename ) );
01917         opts.push_back ( quotedFilename.c_str() );
01918 
01919         modifyDatabase(); // BEFORE run_rpm
01920         run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01921 
01922         string line;
01923         string rpmmsg;
01924         vector<string> configwarnings;
01925         vector<string> errorlines;
01926 
01927         while (systemReadLine(line))
01928           {
01929             if (line.substr(0,2)=="%%")
01930               {
01931                 int percent;
01932                 sscanf (line.c_str () + 2, "%d", &percent);
01933                 report->progress( percent );
01934               }
01935             else
01936               rpmmsg += line+'\n';
01937 
01938             if ( line.substr(0,8) == "warning:" )
01939               {
01940                 configwarnings.push_back(line);
01941               }
01942           }
01943         int rpm_status = systemStatus();
01944 
01945         // evaluate result
01946         for (vector<string>::iterator it = configwarnings.begin();
01947               it != configwarnings.end(); ++it)
01948           {
01949             processConfigFiles(*it, Pathname::basename(filename), " saved as ",
01950                                 // %s = filenames
01951                                 _("rpm saved %s as %s but it was impossible to determine the difference"),
01952                                 // %s = filenames
01953                                 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
01954             processConfigFiles(*it, Pathname::basename(filename), " created as ",
01955                                 // %s = filenames
01956                                 _("rpm created %s as %s but it was impossible to determine the difference"),
01957                                 // %s = filenames
01958                                 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
01959           }
01960 
01961         if ( rpm_status != 0 )
01962           {
01963             // %s = filename of rpm package
01964             progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
01965             progresslog() << _("rpm output:") << endl << rpmmsg << endl;
01966             ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
01967           }
01968         else
01969           {
01970             // %s = filename of rpm package
01971             progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
01972             if ( ! rpmmsg.empty() )
01973               {
01974                 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
01975               }
01976           }
01977       }
01978 
01980       //
01981       //
01982       //        METHOD NAME : RpmDb::removePackage
01983       //        METHOD TYPE : PMError
01984       //
01985       void RpmDb::removePackage( Package::constPtr package, unsigned flags )
01986       {
01987         return removePackage( package->name()
01988                               + "-" + package->edition().asString()
01989                               + "." + package->arch().asString(), flags );
01990       }
01991 
01993       //
01994       //
01995       //        METHOD NAME : RpmDb::removePackage
01996       //        METHOD TYPE : PMError
01997       //
01998       void RpmDb::removePackage( const string & name_r, unsigned flags )
01999       {
02000         callback::SendReport<RpmRemoveReport> report;
02001 
02002         report->start( name_r );
02003 
02004         try
02005           {
02006             doRemovePackage(name_r, flags, report);
02007           }
02008         catch (RpmException & excpt_r)
02009           {
02010             report->finish(excpt_r);
02011             ZYPP_RETHROW(excpt_r);
02012           }
02013         report->finish();
02014       }
02015 
02016 
02017       void RpmDb::doRemovePackage( const string & name_r, unsigned flags, callback::SendReport<RpmRemoveReport> & report )
02018       {
02019         FAILIFNOTINITIALIZED;
02020         CommitLog progresslog;
02021 
02022         MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
02023 
02024         // backup
02025         if ( _packagebackups )
02026           {
02027             // FIXME solve this status report somehow
02028             //      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
02029             if ( ! backupPackage( name_r ) )
02030               {
02031                 ERR << "backup of " << name_r << " failed" << endl;
02032               }
02033             report->progress( 0 );
02034           }
02035         else
02036           {
02037             report->progress( 100 );
02038           }
02039 
02040         // run rpm
02041         RpmArgVec opts;
02042         opts.push_back("-e");
02043         opts.push_back("--allmatches");
02044 
02045         if (flags & RPMINST_NOSCRIPTS)
02046           opts.push_back("--noscripts");
02047         if (flags & RPMINST_NODEPS)
02048           opts.push_back("--nodeps");
02049         if (flags & RPMINST_JUSTDB)
02050           opts.push_back("--justdb");
02051         if (flags & RPMINST_TEST)
02052           opts.push_back ("--test");
02053         if (flags & RPMINST_FORCE)
02054           {
02055             WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
02056           }
02057 
02058         opts.push_back("--");
02059         opts.push_back(name_r.c_str());
02060 
02061         modifyDatabase(); // BEFORE run_rpm
02062         run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
02063 
02064         string line;
02065         string rpmmsg;
02066 
02067         // got no progress from command, so we fake it:
02068         // 5  - command started
02069         // 50 - command completed
02070         // 100 if no error
02071         report->progress( 5 );
02072         while (systemReadLine(line))
02073           {
02074             rpmmsg += line+'\n';
02075           }
02076         report->progress( 50 );
02077         int rpm_status = systemStatus();
02078 
02079         if ( rpm_status != 0 )
02080           {
02081             // %s = name of rpm package
02082             progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
02083             progresslog() << _("rpm output:") << endl << rpmmsg << endl;
02084             ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
02085           }
02086         else
02087           {
02088             progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
02089             if ( ! rpmmsg.empty() )
02090               {
02091                 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
02092               }
02093           }
02094       }
02095 
02097       //
02098       //
02099       //        METHOD NAME : RpmDb::backupPackage
02100       //        METHOD TYPE : bool
02101       //
02102       bool RpmDb::backupPackage( const Pathname & filename )
02103       {
02104         RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
02105         if ( ! h )
02106           return false;
02107 
02108         return backupPackage( h->tag_name() );
02109       }
02110 
02112       //
02113       //
02114       //        METHOD NAME : RpmDb::backupPackage
02115       //        METHOD TYPE : bool
02116       //
02117       bool RpmDb::backupPackage(const string& packageName)
02118       {
02119         CommitLog progresslog;
02120         bool ret = true;
02121         Pathname backupFilename;
02122         Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
02123 
02124         if (_backuppath.empty())
02125           {
02126             INT << "_backuppath empty" << endl;
02127             return false;
02128           }
02129 
02130         FileList fileList;
02131 
02132         if (!queryChangedFiles(fileList, packageName))
02133           {
02134             ERR << "Error while getting changed files for package " <<
02135             packageName << endl;
02136             return false;
02137           }
02138 
02139         if (fileList.size() <= 0)
02140           {
02141             DBG <<  "package " <<  packageName << " not changed -> no backup" << endl;
02142             return true;
02143           }
02144 
02145         if (filesystem::assert_dir(_root + _backuppath) != 0)
02146           {
02147             return false;
02148           }
02149 
02150         {
02151           // build up archive name
02152           time_t currentTime = time(0);
02153           struct tm *currentLocalTime = localtime(&currentTime);
02154 
02155           int date = (currentLocalTime->tm_year + 1900) * 10000
02156           + (currentLocalTime->tm_mon + 1) * 100
02157           + currentLocalTime->tm_mday;
02158 
02159           int num = 0;
02160           do
02161           {
02162             backupFilename = _root + _backuppath
02163             + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
02164 
02165           }
02166           while ( PathInfo(backupFilename).isExist() && num++ < 1000);
02167 
02168           PathInfo pi(filestobackupfile);
02169           if (pi.isExist() && !pi.isFile())
02170             {
02171               ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
02172               return false;
02173             }
02174 
02175           std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
02176 
02177           if (!fp)
02178             {
02179               ERR << "could not open " << filestobackupfile.asString() << endl;
02180               return false;
02181             }
02182 
02183           for (FileList::const_iterator cit = fileList.begin();
02184                 cit != fileList.end(); ++cit)
02185             {
02186               string name = *cit;
02187               if ( name[0] == '/' )
02188                 {
02189                   // remove slash, file must be relative to -C parameter of tar
02190                   name = name.substr( 1 );
02191                 }
02192               DBG << "saving file "<< name << endl;
02193               fp << name << endl;
02194             }
02195           fp.close();
02196 
02197           const char* const argv[] =
02198           {
02199             "tar",
02200             "-czhP",
02201             "-C",
02202             _root.asString().c_str(),
02203             "--ignore-failed-read",
02204             "-f",
02205             backupFilename.asString().c_str(),
02206             "-T",
02207             filestobackupfile.asString().c_str(),
02208             NULL
02209           };
02210 
02211           // execute tar in inst-sys (we dont know if there is a tar below _root !)
02212           ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
02213 
02214           string tarmsg;
02215 
02216           // TODO: its probably possible to start tar with -v and watch it adding
02217           // files to report progress
02218           for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
02219             {
02220               tarmsg+=output;
02221             }
02222 
02223           int ret = tar.close();
02224 
02225           if ( ret != 0)
02226             {
02227               ERR << "tar failed: " << tarmsg << endl;
02228               ret = false;
02229             }
02230           else
02231             {
02232               MIL << "tar backup ok" << endl;
02233               progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
02234             }
02235 
02236           filesystem::unlink(filestobackupfile);
02237         }
02238 
02239           return ret;
02240       }
02241 
02242       void RpmDb::setBackupPath(const Pathname& path)
02243       {
02244         _backuppath = path;
02245       }
02246 
02247     } // namespace rpm
02248   } // namespace target
02249 } // namespace zypp

Generated on Fri Jul 4 16:58:00 2008 for zypp by  doxygen 1.5.0