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

Generated on Thu Sep 14 15:38:53 2006 for zypp by  doxygen 1.4.6