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

Generated on Thu Jul 6 00:07:26 2006 for zypp by  doxygen 1.4.6