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         if ( process->inputFile() )
01694         {
01695           process->setBlocking( false );
01696           FILE * inputfile = process->inputFile();
01697           int    inputfileFd = ::fileno( inputfile );
01698           do
01699           {
01700             /* Watch inputFile to see when it has input. */
01701             fd_set rfds;
01702             FD_ZERO( &rfds );
01703             FD_SET( inputfileFd, &rfds );
01704             
01705             /* Wait up to 5 seconds. */
01706             struct timeval tv;
01707             tv.tv_sec = 5;
01708             tv.tv_usec = 0;
01709 
01710             int retval = select( inputfileFd+1, &rfds, NULL, NULL, &tv );
01711 
01712             if ( retval == -1 )
01713             {
01714               ERR << "select error: " << strerror(errno) << endl;
01715               if ( errno != EINTR )
01716                 return false;
01717             }
01718             else if ( retval )
01719             {
01720               // Data is available now.
01721               static size_t linebuffer_size = 0;        // static because getline allocs
01722               static char * linebuffer = 0;             // and reallocs if buffer is too small
01723               ssize_t nread = getline( &linebuffer, &linebuffer_size, inputfile );
01724               if ( nread == -1 )
01725               {
01726                 if ( ::feof( inputfile ) )
01727                   return line.size(); // in case of pending output
01728               }
01729               else
01730               {
01731                 if ( nread > 0 )
01732                 {
01733                   if ( linebuffer[nread-1] == '\n' )
01734                     --nread;
01735                   line += string( linebuffer, nread );
01736                 }
01737 
01738                 if ( ! ::ferror( inputfile ) || ::feof( inputfile ) )
01739                   return true; // complete line
01740               }
01741               clearerr( inputfile );
01742             }
01743             else
01744             {
01745               // No data within time.
01746                 if ( ! process->running() )
01747                   return false;
01748             }
01749           } while ( true );
01750         }
01751         return false;
01752       }
01753 
01754       /*--------------------------------------------------------------*/
01755       /* Return the exit status of the rpm process, closing the */
01756       /* connection if not already done                         */
01757       /*--------------------------------------------------------------*/
01758       int
01759       RpmDb::systemStatus()
01760       {
01761         if ( process == NULL )
01762           return -1;
01763 
01764         exit_code = process->close();
01765         process->kill();
01766         delete process;
01767         process = 0;
01768 
01769         //   DBG << "exit code " << exit_code << endl;
01770 
01771         return exit_code;
01772       }
01773 
01774       /*--------------------------------------------------------------*/
01775       /* Forcably kill the rpm process                          */
01776       /*--------------------------------------------------------------*/
01777       void
01778       RpmDb::systemKill()
01779       {
01780         if (process) process->kill();
01781       }
01782 
01783 
01784       // generate diff mails for config files
01785       void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
01786       {
01787         string msg = line.substr(9);
01788         string::size_type pos1 = string::npos;
01789         string::size_type pos2 = string::npos;
01790         string file1s, file2s;
01791         Pathname file1;
01792         Pathname file2;
01793 
01794         pos1 = msg.find (typemsg);
01795         for (;;)
01796           {
01797             if ( pos1 == string::npos )
01798               break;
01799 
01800             pos2 = pos1 + strlen (typemsg);
01801 
01802             if (pos2 >= msg.length() )
01803               break;
01804 
01805             file1 = msg.substr (0, pos1);
01806             file2 = msg.substr (pos2);
01807 
01808             file1s = file1.asString();
01809             file2s = file2.asString();
01810 
01811             if (!_root.empty() && _root != "/")
01812               {
01813                 file1 = _root + file1;
01814                 file2 = _root + file2;
01815               }
01816 
01817             string out;
01818             int ret = diffFiles (file1.asString(), file2.asString(), out, 25);
01819             if (ret)
01820               {
01821                 Pathname file = _root + WARNINGMAILPATH;
01822                 if (filesystem::assert_dir(file) != 0)
01823                   {
01824                     ERR << "Could not create " << file.asString() << endl;
01825                     break;
01826                   }
01827                 file += Date(Date::now()).form("config_diff_%Y_%m_%d.log");
01828                 ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
01829                 if (!notify)
01830                   {
01831                     ERR << "Could not open " <<  file << endl;
01832                     break;
01833                   }
01834 
01835                 // Translator: %s = name of an rpm package. A list of diffs follows
01836                 // this message.
01837                 notify << str::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
01838                 if (ret>1)
01839                   {
01840                     ERR << "diff failed" << endl;
01841                     notify << str::form(difffailmsg,
01842                                          file1s.c_str(), file2s.c_str()) << endl;
01843                   }
01844                 else
01845                   {
01846                     notify << str::form(diffgenmsg,
01847                                          file1s.c_str(), file2s.c_str()) << endl;
01848 
01849                     // remove root for the viewer's pleasure (#38240)
01850                     if (!_root.empty() && _root != "/")
01851                       {
01852                         if (out.substr(0,4) == "--- ")
01853                           {
01854                             out.replace(4, file1.asString().length(), file1s);
01855                           }
01856                         string::size_type pos = out.find("\n+++ ");
01857                         if (pos != string::npos)
01858                           {
01859                             out.replace(pos+5, file2.asString().length(), file2s);
01860                           }
01861                       }
01862                     notify << out << endl;
01863                   }
01864                 notify.close();
01865                 notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
01866                 notify.close();
01867               }
01868             else
01869               {
01870                 WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
01871               }
01872             break;
01873           }
01874       }
01875 
01877       //
01878       //
01879       //        METHOD NAME : RpmDb::installPackage
01880       //        METHOD TYPE : PMError
01881       //
01882       void RpmDb::installPackage( const Pathname & filename, unsigned flags )
01883       {
01884         callback::SendReport<RpmInstallReport> report;
01885 
01886         report->start(filename);
01887 
01888         do
01889         try
01890           {
01891             doInstallPackage(filename, flags, report);
01892             report->finish();
01893             break;
01894           }
01895         catch (RpmException & excpt_r)
01896           {
01897             RpmInstallReport::Action user = report->problem( excpt_r );
01898 
01899             if ( user == RpmInstallReport::ABORT )
01900               {
01901                 report->finish( excpt_r );
01902                 ZYPP_RETHROW(excpt_r);
01903               }
01904             else if ( user == RpmInstallReport::IGNORE )
01905               {
01906                 break;
01907               }
01908           }
01909         while (true);
01910       }
01911 
01912       void RpmDb::doInstallPackage( const Pathname & filename, unsigned flags, callback::SendReport<RpmInstallReport> & report )
01913       {
01914         FAILIFNOTINITIALIZED;
01915         CommitLog progresslog;
01916 
01917         MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
01918 
01919 
01920         // backup
01921         if ( _packagebackups )
01922           {
01923             // FIXME      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
01924             if ( ! backupPackage( filename ) )
01925               {
01926                 ERR << "backup of " << filename.asString() << " failed" << endl;
01927               }
01928             // FIXME status handling
01929             report->progress( 0 ); // allow 1% for backup creation.
01930           }
01931         else
01932           {
01933             report->progress( 100 );
01934           }
01935 
01936         // run rpm
01937         RpmArgVec opts;
01938         if (flags & RPMINST_NOUPGRADE)
01939           opts.push_back("-i");
01940         else
01941           opts.push_back("-U");
01942         opts.push_back("--percent");
01943 
01944         if (flags & RPMINST_NODIGEST)
01945           opts.push_back("--nodigest");
01946         if (flags & RPMINST_NOSIGNATURE)
01947           opts.push_back("--nosignature");
01948         if (flags & RPMINST_NODOCS)
01949           opts.push_back ("--excludedocs");
01950         if (flags & RPMINST_NOSCRIPTS)
01951           opts.push_back ("--noscripts");
01952         if (flags & RPMINST_FORCE)
01953           opts.push_back ("--force");
01954         if (flags & RPMINST_NODEPS)
01955           opts.push_back ("--nodeps");
01956         if (flags & RPMINST_IGNORESIZE)
01957           opts.push_back ("--ignoresize");
01958         if (flags & RPMINST_JUSTDB)
01959           opts.push_back ("--justdb");
01960         if (flags & RPMINST_TEST)
01961           opts.push_back ("--test");
01962 
01963         opts.push_back("--");
01964 
01965         // rpm requires additional quoting of special chars:
01966         std::string quotedFilename( rpmQuoteFilename( filename ) );
01967         opts.push_back ( quotedFilename.c_str() );
01968 
01969         modifyDatabase(); // BEFORE run_rpm
01970         run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
01971 
01972         string line;
01973         string rpmmsg;
01974         vector<string> configwarnings;
01975         vector<string> errorlines;
01976 
01977         while (systemReadLine(line))
01978           {
01979             if (line.substr(0,2)=="%%")
01980               {
01981                 int percent;
01982                 sscanf (line.c_str () + 2, "%d", &percent);
01983                 report->progress( percent );
01984               }
01985             else
01986               rpmmsg += line+'\n';
01987 
01988             if ( line.substr(0,8) == "warning:" )
01989               {
01990                 configwarnings.push_back(line);
01991               }
01992           }
01993         int rpm_status = systemStatus();
01994 
01995         // evaluate result
01996         for (vector<string>::iterator it = configwarnings.begin();
01997               it != configwarnings.end(); ++it)
01998           {
01999             processConfigFiles(*it, Pathname::basename(filename), " saved as ",
02000                                 // %s = filenames
02001                                 _("rpm saved %s as %s but it was impossible to determine the difference"),
02002                                 // %s = filenames
02003                                 _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
02004             processConfigFiles(*it, Pathname::basename(filename), " created as ",
02005                                 // %s = filenames
02006                                 _("rpm created %s as %s but it was impossible to determine the difference"),
02007                                 // %s = filenames
02008                                 _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
02009           }
02010 
02011         if ( rpm_status != 0 )
02012           {
02013             // %s = filename of rpm package
02014             progresslog(/*timestamp*/true) << str::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
02015             progresslog() << _("rpm output:") << endl << rpmmsg << endl;
02016             ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
02017           }
02018         else
02019           {
02020             // %s = filename of rpm package
02021             progresslog(/*timestamp*/true) << str::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
02022             if ( ! rpmmsg.empty() )
02023               {
02024                 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
02025               }
02026           }
02027       }
02028 
02030       //
02031       //
02032       //        METHOD NAME : RpmDb::removePackage
02033       //        METHOD TYPE : PMError
02034       //
02035       void RpmDb::removePackage( Package::constPtr package, unsigned flags )
02036       {
02037         // clear epoch, see bnc #372323
02038         Edition tmp(package->edition().version(), package->edition().release(), Edition::noepoch);
02039 
02040         return removePackage( package->name()
02041                               + "-" + tmp.asString()
02042                               + "." + package->arch().asString(), flags );
02043       }
02044 
02046       //
02047       //
02048       //        METHOD NAME : RpmDb::removePackage
02049       //        METHOD TYPE : PMError
02050       //
02051       void RpmDb::removePackage( const string & name_r, unsigned flags )
02052       {
02053         callback::SendReport<RpmRemoveReport> report;
02054 
02055         report->start( name_r );
02056 
02057         try
02058           {
02059             doRemovePackage(name_r, flags, report);
02060           }
02061         catch (RpmException & excpt_r)
02062           {
02063             report->finish(excpt_r);
02064             ZYPP_RETHROW(excpt_r);
02065           }
02066         report->finish();
02067       }
02068 
02069 
02070       void RpmDb::doRemovePackage( const string & name_r, unsigned flags, callback::SendReport<RpmRemoveReport> & report )
02071       {
02072         FAILIFNOTINITIALIZED;
02073         CommitLog progresslog;
02074 
02075         MIL << "RpmDb::doRemovePackage(" << name_r << "," << flags << ")" << endl;
02076 
02077         // backup
02078         if ( _packagebackups )
02079           {
02080             // FIXME solve this status report somehow
02081             //      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
02082             if ( ! backupPackage( name_r ) )
02083               {
02084                 ERR << "backup of " << name_r << " failed" << endl;
02085               }
02086             report->progress( 0 );
02087           }
02088         else
02089           {
02090             report->progress( 100 );
02091           }
02092 
02093         // run rpm
02094         RpmArgVec opts;
02095         opts.push_back("-e");
02096         opts.push_back("--allmatches");
02097 
02098         if (flags & RPMINST_NOSCRIPTS)
02099           opts.push_back("--noscripts");
02100         if (flags & RPMINST_NODEPS)
02101           opts.push_back("--nodeps");
02102         if (flags & RPMINST_JUSTDB)
02103           opts.push_back("--justdb");
02104         if (flags & RPMINST_TEST)
02105           opts.push_back ("--test");
02106         if (flags & RPMINST_FORCE)
02107           {
02108             WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
02109           }
02110 
02111         opts.push_back("--");
02112         opts.push_back(name_r.c_str());
02113 
02114         modifyDatabase(); // BEFORE run_rpm
02115         run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
02116 
02117         string line;
02118         string rpmmsg;
02119 
02120         // got no progress from command, so we fake it:
02121         // 5  - command started
02122         // 50 - command completed
02123         // 100 if no error
02124         report->progress( 5 );
02125         while (systemReadLine(line))
02126           {
02127             rpmmsg += line+'\n';
02128           }
02129         report->progress( 50 );
02130         int rpm_status = systemStatus();
02131 
02132         if ( rpm_status != 0 )
02133           {
02134             // %s = name of rpm package
02135             progresslog(/*timestamp*/true) << str::form(_("%s remove failed"), name_r.c_str()) << endl;
02136             progresslog() << _("rpm output:") << endl << rpmmsg << endl;
02137             ZYPP_THROW(RpmSubprocessException(string("RPM failed: ") + rpmmsg));
02138           }
02139         else
02140           {
02141             progresslog(/*timestamp*/true) << str::form(_("%s remove ok"), name_r.c_str()) << endl;
02142             if ( ! rpmmsg.empty() )
02143               {
02144                 progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
02145               }
02146           }
02147       }
02148 
02150       //
02151       //
02152       //        METHOD NAME : RpmDb::backupPackage
02153       //        METHOD TYPE : bool
02154       //
02155       bool RpmDb::backupPackage( const Pathname & filename )
02156       {
02157         RpmHeader::constPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
02158         if ( ! h )
02159           return false;
02160 
02161         return backupPackage( h->tag_name() );
02162       }
02163 
02165       //
02166       //
02167       //        METHOD NAME : RpmDb::backupPackage
02168       //        METHOD TYPE : bool
02169       //
02170       bool RpmDb::backupPackage(const string& packageName)
02171       {
02172         CommitLog progresslog;
02173         bool ret = true;
02174         Pathname backupFilename;
02175         Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
02176 
02177         if (_backuppath.empty())
02178           {
02179             INT << "_backuppath empty" << endl;
02180             return false;
02181           }
02182 
02183         FileList fileList;
02184 
02185         if (!queryChangedFiles(fileList, packageName))
02186           {
02187             ERR << "Error while getting changed files for package " <<
02188             packageName << endl;
02189             return false;
02190           }
02191 
02192         if (fileList.size() <= 0)
02193           {
02194             DBG <<  "package " <<  packageName << " not changed -> no backup" << endl;
02195             return true;
02196           }
02197 
02198         if (filesystem::assert_dir(_root + _backuppath) != 0)
02199           {
02200             return false;
02201           }
02202 
02203         {
02204           // build up archive name
02205           time_t currentTime = time(0);
02206           struct tm *currentLocalTime = localtime(&currentTime);
02207 
02208           int date = (currentLocalTime->tm_year + 1900) * 10000
02209           + (currentLocalTime->tm_mon + 1) * 100
02210           + currentLocalTime->tm_mday;
02211 
02212           int num = 0;
02213           do
02214           {
02215             backupFilename = _root + _backuppath
02216             + str::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
02217 
02218           }
02219           while ( PathInfo(backupFilename).isExist() && num++ < 1000);
02220 
02221           PathInfo pi(filestobackupfile);
02222           if (pi.isExist() && !pi.isFile())
02223             {
02224               ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
02225               return false;
02226             }
02227 
02228           std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
02229 
02230           if (!fp)
02231             {
02232               ERR << "could not open " << filestobackupfile.asString() << endl;
02233               return false;
02234             }
02235 
02236           for (FileList::const_iterator cit = fileList.begin();
02237                 cit != fileList.end(); ++cit)
02238             {
02239               string name = *cit;
02240               if ( name[0] == '/' )
02241                 {
02242                   // remove slash, file must be relative to -C parameter of tar
02243                   name = name.substr( 1 );
02244                 }
02245               DBG << "saving file "<< name << endl;
02246               fp << name << endl;
02247             }
02248           fp.close();
02249 
02250           const char* const argv[] =
02251           {
02252             "tar",
02253             "-czhP",
02254             "-C",
02255             _root.asString().c_str(),
02256             "--ignore-failed-read",
02257             "-f",
02258             backupFilename.asString().c_str(),
02259             "-T",
02260             filestobackupfile.asString().c_str(),
02261             NULL
02262           };
02263 
02264           // execute tar in inst-sys (we dont know if there is a tar below _root !)
02265           ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
02266 
02267           string tarmsg;
02268 
02269           // TODO: its probably possible to start tar with -v and watch it adding
02270           // files to report progress
02271           for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
02272             {
02273               tarmsg+=output;
02274             }
02275 
02276           int ret = tar.close();
02277 
02278           if ( ret != 0)
02279             {
02280               ERR << "tar failed: " << tarmsg << endl;
02281               ret = false;
02282             }
02283           else
02284             {
02285               MIL << "tar backup ok" << endl;
02286               progresslog(/*timestamp*/true) << str::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
02287             }
02288 
02289           filesystem::unlink(filestobackupfile);
02290         }
02291 
02292           return ret;
02293       }
02294 
02295       void RpmDb::setBackupPath(const Pathname& path)
02296       {
02297         _backuppath = path;
02298       }
02299 
02300     } // namespace rpm
02301   } // namespace target
02302 } // namespace zypp

Generated on Wed Feb 16 18:51:52 2011 for zypp by  doxygen 1.4.6