00001
00002
00003
00004
00005
00006
00007
00008
00012 #include "librpm.h"
00013
00014 #include <iostream>
00015
00016 #include "zypp/base/Logger.h"
00017 #include "zypp/target/rpm/librpmDb.h"
00018 #include "zypp/target/rpm/RpmHeader.h"
00019 #include "zypp/target/rpm/RpmException.h"
00020
00021 using namespace std;
00022
00023 namespace zypp {
00024 namespace target {
00025 namespace rpm {
00027
00028
00032 class librpmDb::D {
00033 D & operator=( const D & );
00034 D ( const D & );
00035 public:
00036
00037 const Pathname _root;
00038 const Pathname _dbPath;
00039 rpmdb _db;
00040 shared_ptr<RpmException> _error;
00041
00042 friend ostream & operator<<( ostream & str, const D & obj ) {
00043 str << "{" << obj._error << "(" << obj._root << ")" << obj._dbPath << "}";
00044 return str;
00045 }
00046
00047 D( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
00048 : _root ( root_r )
00049 , _dbPath( dbPath_r )
00050 , _db ( 0 )
00051 {
00052 _error.reset();
00053
00054 ::addMacro( NULL, "_dbpath", NULL, _dbPath.asString().c_str(), RMIL_CMDLINE );
00055 const char * root = ( _root == "/" ? NULL : _root.asString().c_str() );
00056 int perms = 0644;
00057
00058
00059 PathInfo master( _root + _dbPath + "Packages" );
00060 if ( ! master.isFile() ) {
00061
00062 int res = ::rpmdbInit( root, perms );
00063 if ( res ) {
00064 ERR << "rpmdbInit error(" << res << "): " << *this << endl;
00065 _error = shared_ptr<RpmInitException>(new RpmInitException(_root, _dbPath));
00066 ZYPP_THROW(*_error);
00067 }
00068 }
00069
00070
00071 int res = ::rpmdbOpen( root, &_db, (readonly_r ? O_RDONLY : O_RDWR ), perms );
00072 if ( res || !_db ) {
00073 if ( _db ) {
00074 ::rpmdbClose( _db );
00075 _db = 0;
00076 }
00077 ERR << "rpmdbOpen error(" << res << "): " << *this << endl;
00078 _error = shared_ptr<RpmDbOpenException>(new RpmDbOpenException(_root, _dbPath));
00079 ZYPP_THROW(*_error);
00080 return;
00081 }
00082
00083 DBG << "DBACCESS " << *this << endl;
00084 }
00085
00086 ~D() {
00087 if ( _db ) {
00088 ::rpmdbClose( _db );
00089 }
00090 }
00091 };
00092
00094
00096
00097
00098
00100
00101 Pathname librpmDb::_defaultRoot ( "/" );
00102 Pathname librpmDb::_defaultDbPath( "/var/lib/rpm" );
00103 librpmDb::constPtr librpmDb::_defaultDb;
00104 bool librpmDb::_dbBlocked ( true );
00105
00107
00108
00109
00110
00111
00112 bool librpmDb::globalInit()
00113 {
00114 static bool initialized = false;
00115
00116 if ( initialized )
00117 return true;
00118
00119 int rc = ::rpmReadConfigFiles( NULL, NULL );
00120 if ( rc ) {
00121 ERR << "rpmReadConfigFiles returned " << rc << endl;
00122 return false;
00123 }
00124
00125
00126 ::addMacro( NULL, "_rpmdb_rebuild", NULL, "%{nil}", RMIL_CMDLINE );
00127
00128 initialized = true;
00129
00130 #define OUTVAL(n) << " (" #n ":" << expand( "%{" #n "}" ) << ")"
00131 MIL << "librpm init done:"
00132 OUTVAL(_target)
00133 OUTVAL(_dbpath)
00134 << endl;
00135 #undef OUTVAL
00136 return initialized;
00137 }
00138
00140
00141
00142
00143
00144
00145 std::string librpmDb::expand( const std::string & macro_r )
00146 {
00147 if ( ! globalInit() )
00148 return macro_r;
00149
00150 char * val = ::rpmExpand( macro_r.c_str(), NULL );
00151 if ( !val )
00152 return "";
00153
00154 string ret( val );
00155 free( val );
00156 return ret;
00157 }
00158
00160
00161
00162
00163
00164
00165 librpmDb * librpmDb::newLibrpmDb( Pathname root_r, Pathname dbPath_r, bool readonly_r )
00166 {
00167
00168 if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
00169 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
00170 }
00171
00172
00173 if ( ! globalInit() ) {
00174 ZYPP_THROW(GlobalRpmInitException());
00175 }
00176
00177
00178 librpmDb * ret = 0;
00179 try {
00180 ret = new librpmDb( root_r, dbPath_r, readonly_r );
00181 }
00182 catch (const RpmException & excpt_r)
00183 {
00184 ZYPP_CAUGHT(excpt_r);
00185 delete ret;
00186 ret = 0;
00187 ZYPP_RETHROW(excpt_r);
00188 }
00189 return ret;
00190 }
00191
00193
00194
00195
00196
00197
00198 void librpmDb::dbAccess( const Pathname & root_r, const Pathname & dbPath_r )
00199 {
00200
00201 if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
00202 ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
00203 }
00204
00205 if ( _defaultDb ) {
00206
00207 if ( _defaultRoot == root_r && _defaultDbPath == dbPath_r )
00208 return;
00209 else {
00210 ZYPP_THROW(RpmDbAlreadyOpenException(_defaultRoot, _defaultDbPath, root_r, dbPath_r));
00211 }
00212 }
00213
00214
00215 _defaultRoot = root_r;
00216 _defaultDbPath = dbPath_r;
00217 MIL << "Set new database location: " << stringPath( _defaultRoot, _defaultDbPath ) << endl;
00218
00219 return dbAccess();
00220 }
00221
00223
00224
00225
00226
00227
00228 void librpmDb::dbAccess()
00229 {
00230 if ( _dbBlocked ) {
00231 ZYPP_THROW(RpmAccessBlockedException(_defaultRoot, _defaultDbPath));
00232 }
00233
00234 if ( !_defaultDb ) {
00235
00236 _defaultDb = newLibrpmDb( _defaultRoot, _defaultDbPath, true );
00237 }
00238 }
00239
00241
00242
00243
00244
00245
00246 void librpmDb::dbAccess( librpmDb::constPtr & ptr_r )
00247 {
00248 try {
00249 dbAccess();
00250 }
00251 catch (const RpmException & excpt_r)
00252 {
00253 ZYPP_CAUGHT(excpt_r);
00254 ptr_r = 0;
00255 ZYPP_RETHROW(excpt_r);
00256 }
00257 ptr_r = _defaultDb;
00258 }
00259
00261
00262
00263
00264
00265
00266 unsigned librpmDb::dbRelease( bool force_r )
00267 {
00268 if ( !_defaultDb ) {
00269 return 0;
00270 }
00271
00272 unsigned outstanding = _defaultDb->refCount() - 1;
00273
00274 switch ( outstanding ) {
00275 default:
00276 if ( !force_r ) {
00277 DBG << "dbRelease: keep access, outstanding " << outstanding << endl;
00278 break;
00279 }
00280
00281 case 0:
00282 DBG << "dbRelease: release" << (force_r && outstanding ? "(forced)" : "")
00283 << ", outstanding " << outstanding << endl;
00284
00285 _defaultDb->_d._error = shared_ptr<RpmAccessBlockedException>(new RpmAccessBlockedException(_defaultDb->_d._root, _defaultDb->_d._dbPath));
00286
00287 _defaultDb = 0;
00288 break;
00289 }
00290
00291 return outstanding;
00292 }
00293
00295
00296
00297
00298
00299
00300 unsigned librpmDb::blockAccess()
00301 {
00302 MIL << "Block access" << endl;
00303 _dbBlocked = true;
00304 return dbRelease( true );
00305 }
00306
00308
00309
00310
00311
00312
00313 void librpmDb::unblockAccess()
00314 {
00315 MIL << "Unblock access" << endl;
00316 _dbBlocked = false;
00317 }
00318
00320
00321
00322
00323
00324
00325 ostream & librpmDb::dumpState( ostream & str )
00326 {
00327 if ( !_defaultDb ) {
00328 return str << "[librpmDb " << (_dbBlocked?"BLOCKED":"CLOSED") << " " << stringPath( _defaultRoot, _defaultDbPath ) << "]";
00329 }
00330 return str << "[" << _defaultDb << "]";
00331 }
00332
00334
00335
00336
00338
00340
00341
00342
00343
00344
00345
00346
00347 librpmDb::librpmDb( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
00348 : _d( * new D( root_r, dbPath_r, readonly_r ) )
00349 {
00350 }
00351
00353
00354
00355
00356
00357
00358
00359
00360 librpmDb::~librpmDb()
00361 {
00362 delete &_d;
00363 }
00364
00366
00367
00368
00369
00370
00371 void librpmDb::unref_to( unsigned refCount_r ) const
00372 {
00373 if ( refCount_r == 1 ) {
00374 dbRelease();
00375 }
00376 }
00377
00379
00380
00381
00382
00383
00384 const Pathname & librpmDb::root() const
00385 {
00386 return _d._root;
00387 }
00388
00390
00391
00392
00393
00394
00395 const Pathname & librpmDb::dbPath() const
00396 {
00397 return _d._dbPath;
00398 }
00399
00401
00402
00403
00404
00405
00406 shared_ptr<RpmException> librpmDb::error() const
00407 {
00408 return _d._error;
00409 }
00410
00412
00413
00414
00415
00416
00417 bool librpmDb::empty() const
00418 {
00419 return( valid() && ! *db_const_iterator( this ) );
00420 }
00421
00423
00424
00425
00426
00427
00428 unsigned librpmDb::size() const
00429 {
00430 unsigned count = 0;
00431 if ( valid() )
00432 {
00433 dbiIndex dbi = dbiOpen( _d._db, RPMTAG_NAME, 0 );
00434 if ( dbi )
00435 {
00436 DBC * dbcursor = 0;
00437 dbiCopen( dbi, dbi->dbi_txnid, &dbcursor, 0 );
00438
00439 DBT key, data;
00440 memset( &key, 0, sizeof(key) );
00441 memset( &data, 0, sizeof(data) );
00442 while ( dbiGet( dbi, dbcursor, &key, &data, DB_NEXT ) == 0 )
00443 count += data.size / dbi->dbi_jlen;
00444
00445 dbiCclose( dbi, dbcursor, 0 );
00446
00447 }
00448 }
00449 return count;
00450 }
00451
00453
00454
00455
00456
00457
00458 void * librpmDb::dont_call_it() const
00459 {
00460 return _d._db;
00461 }
00462
00464
00465
00466
00467
00468
00469
00470
00471 ostream & librpmDb::dumpOn( ostream & str ) const
00472 {
00473 ReferenceCounted::dumpOn( str ) << _d;
00474 return str;
00475 }
00476
00478
00479
00480
00482
00484
00485
00486
00487
00488
00489 librpmDb::DbDirInfo::DbDirInfo( const Pathname & root_r, const Pathname & dbPath_r )
00490 : _root( root_r )
00491 , _dbPath( dbPath_r )
00492 {
00493
00494 if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
00495 ERR << "Relative path for root(" << _root << ") or dbPath(" << _dbPath << ")" << endl;
00496 } else {
00497 _dbDir ( _root + _dbPath );
00498 _dbV4 ( _dbDir.path() + "Packages" );
00499 _dbV3 ( _dbDir.path() + "packages.rpm" );
00500 _dbV3ToV4( _dbDir.path() + "packages.rpm3" );
00501 DBG << *this << endl;
00502 }
00503 }
00504
00506
00507
00508
00509
00510
00511 void librpmDb::DbDirInfo::restat()
00512 {
00513 _dbDir();
00514 _dbV4();
00515 _dbV3();
00516 _dbV3ToV4();
00517 DBG << *this << endl;
00518 }
00519
00520
00521
00522
00523
00524
00525
00526 std::ostream & operator<<( std::ostream & str, const librpmDb::DbDirInfo & obj )
00527 {
00528 if ( obj.illegalArgs() ) {
00529 str << "ILLEGAL: '(" << obj.root() << ")" << obj.dbPath() << "'";
00530 } else {
00531 str << "'(" << obj.root() << ")" << obj.dbPath() << "':" << endl;
00532 str << " Dir: " << obj._dbDir << endl;
00533 str << " V4: " << obj._dbV4 << endl;
00534 str << " V3: " << obj._dbV3 << endl;
00535 str << " V3ToV4: " << obj._dbV3ToV4;
00536 }
00537 return str;
00538 }
00539
00541
00542
00546 class librpmDb::db_const_iterator::D {
00547 D & operator=( const D & );
00548 D ( const D & );
00549 public:
00550
00551 librpmDb::constPtr _dbptr;
00552 shared_ptr<RpmException> _dberr;
00553
00554 RpmHeader::constPtr _hptr;
00555 rpmdbMatchIterator _mi;
00556
00557 D( librpmDb::constPtr dbptr_r )
00558 : _dbptr( dbptr_r )
00559 , _mi( 0 )
00560 {
00561 if ( !_dbptr ) {
00562 try {
00563 librpmDb::dbAccess( _dbptr );
00564 }
00565 catch (const RpmException & excpt_r)
00566 {
00567 ZYPP_CAUGHT(excpt_r);
00568 }
00569 if ( !_dbptr ) {
00570 WAR << "No database access: " << _dberr << endl;
00571 }
00572 } else {
00573 destroy();
00574 }
00575 }
00576
00577 ~D() {
00578 if ( _mi ) {
00579 ::rpmdbFreeIterator( _mi );
00580 }
00581 }
00582
00587 bool create( int rpmtag, const void * keyp = NULL, size_t keylen = 0 ) {
00588 destroy();
00589 if ( ! _dbptr )
00590 return false;
00591 _mi = ::rpmdbInitIterator( _dbptr->_d._db, rpmTag(rpmtag), keyp, keylen );
00592 return _mi;
00593 }
00594
00599 bool destroy() {
00600 if ( _mi ) {
00601 _mi = ::rpmdbFreeIterator( _mi );
00602 _hptr = 0;
00603 }
00604 if ( _dbptr && _dbptr->error() ) {
00605 _dberr = _dbptr->error();
00606 WAR << "Lost database access: " << _dberr << endl;
00607 _dbptr = 0;
00608 }
00609 return false;
00610 }
00611
00616 bool advance() {
00617 if ( !_mi )
00618 return false;
00619 Header h = ::rpmdbNextIterator( _mi );
00620 if ( ! h ) {
00621 destroy();
00622 return false;
00623 }
00624 _hptr = new RpmHeader( h );
00625 return true;
00626 }
00627
00631 bool init( int rpmtag, const void * keyp = NULL, size_t keylen = 0 ) {
00632 if ( ! create( rpmtag, keyp, keylen ) )
00633 return false;
00634 return advance();
00635 }
00636
00641 bool set( int off_r ) {
00642 if ( ! create( RPMDBI_PACKAGES ) )
00643 return false;
00644 #warning TESTCASE: rpmdbAppendIterator and (non)sequential access?
00645 ::rpmdbAppendIterator( _mi, &off_r, 1 );
00646 return advance();
00647 }
00648
00649 unsigned offset() {
00650 return( _mi ? ::rpmdbGetIteratorOffset( _mi ) : 0 );
00651 }
00652
00653 int size() {
00654 if ( !_mi )
00655 return 0;
00656 int ret = ::rpmdbGetIteratorCount( _mi );
00657 #warning TESTCASE: rpmdbGetIteratorCount returns 0 on sequential access?
00658 return( ret ? ret : -1 );
00659 }
00660 };
00661
00663
00665
00666
00667
00669
00671
00672
00673
00674
00675
00676 librpmDb::db_const_iterator::db_const_iterator( librpmDb::constPtr dbptr_r )
00677 : _d( * new D( dbptr_r ) )
00678 {
00679 findAll();
00680 }
00681
00683
00684
00685
00686
00687
00688 librpmDb::db_const_iterator::~db_const_iterator()
00689 {
00690 delete &_d;
00691 }
00692
00694
00695
00696
00697
00698
00699 void librpmDb::db_const_iterator::operator++()
00700 {
00701 _d.advance();
00702 }
00703
00705
00706
00707
00708
00709
00710 unsigned librpmDb::db_const_iterator::dbHdrNum() const
00711 {
00712 return _d.offset();
00713 }
00714
00716
00717
00718
00719
00720
00721 const RpmHeader::constPtr & librpmDb::db_const_iterator::operator*() const
00722 {
00723 return _d._hptr;
00724 }
00725
00727
00728
00729
00730
00731
00732 shared_ptr<RpmException> librpmDb::db_const_iterator::dbError() const
00733 {
00734 if ( _d._dbptr )
00735 return _d._dbptr->error();
00736
00737 return _d._dberr;
00738 }
00739
00740
00741
00742
00743
00744
00745
00746 ostream & operator<<( ostream & str, const librpmDb::db_const_iterator & obj )
00747 {
00748 str << "db_const_iterator(" << obj._d._dbptr
00749 << " Size:" << obj._d.size()
00750 << " HdrNum:" << obj._d.offset()
00751 << ")";
00752 return str;
00753 }
00754
00756
00757
00758
00759
00760
00761 bool librpmDb::db_const_iterator::findAll()
00762 {
00763 return _d.init( RPMDBI_PACKAGES );
00764 }
00765
00767
00768
00769
00770
00771
00772 bool librpmDb::db_const_iterator::findByFile( const std::string & file_r )
00773 {
00774 return _d.init( RPMTAG_BASENAMES, file_r.c_str() );
00775 }
00776
00778
00779
00780
00781
00782
00783 bool librpmDb::db_const_iterator::findByProvides( const std::string & tag_r )
00784 {
00785 return _d.init( RPMTAG_PROVIDENAME, tag_r.c_str() );
00786 }
00787
00789
00790
00791
00792
00793
00794 bool librpmDb::db_const_iterator::findByRequiredBy( const std::string & tag_r )
00795 {
00796 return _d.init( RPMTAG_REQUIRENAME, tag_r.c_str() );
00797 }
00798
00800
00801
00802
00803
00804
00805 bool librpmDb::db_const_iterator::findByConflicts( const std::string & tag_r )
00806 {
00807 return _d.init( RPMTAG_CONFLICTNAME, tag_r.c_str() );
00808 }
00809
00811
00812
00813
00814
00815
00816 bool librpmDb::db_const_iterator::findByName( const string & name_r )
00817 {
00818 return _d.init( RPMTAG_NAME, name_r.c_str() );
00819 }
00820
00822
00823
00824
00825
00826
00827 bool librpmDb::db_const_iterator::findPackage( const string & name_r )
00828 {
00829 if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
00830 return false;
00831
00832 if ( _d.size() == 1 )
00833 return true;
00834
00835
00836 int match = 0;
00837 time_t itime = 0;
00838 for ( ; operator*(); operator++() ) {
00839 if ( operator*()->tag_installtime() > itime ) {
00840 match = _d.offset();
00841 itime = operator*()->tag_installtime();
00842 }
00843 }
00844
00845 return _d.set( match );
00846 }
00847
00849
00850
00851
00852
00853
00854 bool librpmDb::db_const_iterator::findPackage( const std::string & name_r, const Edition & ed_r )
00855 {
00856 if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
00857 return false;
00858
00859 for ( ; operator*(); operator++() ) {
00860 if ( ed_r == operator*()->tag_edition() ) {
00861 int match = _d.offset();
00862 return _d.set( match );
00863 }
00864 }
00865
00866 return _d.destroy();
00867 }
00868
00870
00871
00872
00873
00874
00875 bool librpmDb::db_const_iterator::findPackage( const Package::constPtr & which_r )
00876 {
00877 if ( ! which_r )
00878 return _d.destroy();
00879
00880 return findPackage( which_r->name(), which_r->edition() );
00881 }
00882
00883 }
00884 }
00885 }