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

Generated on Tue Nov 28 16:49:33 2006 for zypp by  doxygen 1.5.0