libzypp  10.5.0
PoolImpl.cc
Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <iostream>
00013 #include <fstream>
00014 #include <boost/mpl/int.hpp>
00015 
00016 #include "zypp/base/Easy.h"
00017 #include "zypp/base/LogTools.h"
00018 #include "zypp/base/Gettext.h"
00019 #include "zypp/base/Exception.h"
00020 #include "zypp/base/Measure.h"
00021 #include "zypp/base/WatchFile.h"
00022 #include "zypp/base/Sysconfig.h"
00023 #include "zypp/base/IOStream.h"
00024 
00025 #include "zypp/ZConfig.h"
00026 
00027 #include "zypp/sat/detail/PoolImpl.h"
00028 #include "zypp/sat/Pool.h"
00029 #include "zypp/Capability.h"
00030 #include "zypp/Locale.h"
00031 #include "zypp/PoolItem.h"
00032 
00033 #include "zypp/target/modalias/Modalias.h"
00034 #include "zypp/media/MediaPriority.h"
00035 
00036 extern "C"
00037 {
00038 // Workaround libsolv project not providing a common include
00039 // directory. (the -devel package does, but the git repo doesn't).
00040 // #include <solv/repo_helix.h>
00041 void repo_add_helix( ::Repo *repo, FILE *fp, int flags );
00042 }
00043 
00044 using std::endl;
00045 
00046 #undef  ZYPP_BASE_LOGGER_LOGGROUP
00047 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::satpool"
00048 
00049 // ///////////////////////////////////////////////////////////////////
00050 namespace zypp
00051 { 
00052 
00053   namespace sat
00054   { 
00055 
00057     namespace detail
00058     { 
00059 
00060       // MPL checks for satlib constants we redefine to avoid
00061       // includes and defines.
00062       BOOST_MPL_ASSERT_RELATION( noId,                 ==, STRID_NULL );
00063       BOOST_MPL_ASSERT_RELATION( emptyId,              ==, STRID_EMPTY );
00064 
00065       BOOST_MPL_ASSERT_RELATION( noSolvableId,         ==, ID_NULL );
00066       BOOST_MPL_ASSERT_RELATION( systemSolvableId,     ==, SYSTEMSOLVABLE );
00067 
00068       BOOST_MPL_ASSERT_RELATION( solvablePrereqMarker, ==, SOLVABLE_PREREQMARKER );
00069       BOOST_MPL_ASSERT_RELATION( solvableFileMarker,   ==, SOLVABLE_FILEMARKER );
00070 
00071       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_AND,       ==, REL_AND );
00072       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_OR,        ==, REL_OR );
00073       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_WITH,      ==, REL_WITH );
00074       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_NAMESPACE, ==, REL_NAMESPACE );
00075       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_ARCH,      ==, REL_ARCH );
00076 
00078 
00079       const std::string & PoolImpl::systemRepoAlias()
00080       {
00081         static const std::string _val( "@System" );
00082         return _val;
00083       }
00084 
00085       const Pathname & sysconfigStoragePath()
00086       {
00087         static const Pathname _val( "/etc/sysconfig/storage" );
00088         return _val;
00089       }
00090 
00091 
00093 
00094       static void logSat( struct _Pool *, void *data, int type, const char *logString )
00095       {
00096           if ( type & (SOLV_FATAL|SOLV_ERROR) ) {
00097             _ERR("libsolv") << logString;
00098           } else if ( type & SOLV_DEBUG_STATS ) {
00099             _DBG("libsolv") << logString;
00100           } else {
00101             _MIL("libsolv") << logString;
00102           }
00103       }
00104 
00105       detail::IdType PoolImpl::nsCallback( struct _Pool *, void * data, detail::IdType lhs, detail::IdType rhs )
00106       {
00107         // lhs:    the namespace identifier, e.g. NAMESPACE:MODALIAS
00108         // rhs:    the value, e.g. pci:v0000104Cd0000840[01]sv*sd*bc*sc*i*
00109         // return: 0 if not supportded
00110         //         1 if supported by the system
00111         //        -1  AFAIK it's also possible to return a list of solvables that support it, but don't know how.
00112 
00113         static const detail::IdType RET_unsupported    = 0;
00114         static const detail::IdType RET_systemProperty = 1;
00115         switch ( lhs )
00116         {
00117           case NAMESPACE_LANGUAGE:
00118           {
00119             static IdString en( "en" );
00120             const std::tr1::unordered_set<IdString> & locale2Solver( reinterpret_cast<PoolImpl*>(data)->_locale2Solver );
00121             if ( locale2Solver.empty() )
00122             {
00123               return rhs == en.id() ? RET_systemProperty : RET_unsupported;
00124             }
00125             return locale2Solver.find( IdString(rhs) ) != locale2Solver.end() ? RET_systemProperty : RET_unsupported;
00126           }
00127           break;
00128 
00129           case NAMESPACE_MODALIAS:
00130           {
00131             // modalias strings in capability may be hexencoded because rpm does not allow
00132             // ',', ' ' or other special chars.
00133             return target::Modalias::instance().query( str::hexdecode( IdString(rhs).c_str() ) )
00134                 ? RET_systemProperty
00135               : RET_unsupported;
00136           }
00137           break;
00138 
00139           case NAMESPACE_FILESYSTEM:
00140           {
00141             const std::set<std::string> & requiredFilesystems( reinterpret_cast<PoolImpl*>(data)->requiredFilesystems() );
00142             return requiredFilesystems.find( IdString(rhs).asString() ) != requiredFilesystems.end() ? RET_systemProperty : RET_unsupported;
00143           }
00144           break;
00145 
00146           case NAMESPACE_PRODUCTBUDDY:
00147           {
00148             PoolItem pi( (Solvable(rhs)) );
00149             return( pi ? pi.buddy().id() : noId );
00150           }
00151 
00152           break;
00153         }
00154 
00155         WAR << "Unhandled " << Capability( lhs ) << " vs. " << Capability( rhs ) << endl;
00156         return RET_unsupported;
00157       }
00158 
00160       //
00161       //        METHOD NAME : PoolMember::myPool
00162       //        METHOD TYPE : PoolImpl
00163       //
00164       PoolImpl & PoolMember::myPool()
00165       {
00166         static PoolImpl _global;
00167         return _global;
00168       }
00169 
00171       //
00172       //        METHOD NAME : PoolImpl::PoolImpl
00173       //        METHOD TYPE : Ctor
00174       //
00175       PoolImpl::PoolImpl()
00176       : _pool( ::pool_create() )
00177       {
00178         MIL << "Creating sat-pool." << endl;
00179         if ( ! _pool )
00180         {
00181           ZYPP_THROW( Exception( _("Can not create sat-pool.") ) );
00182         }
00183         // initialialize logging
00184         if ( getenv("ZYPP_LIBSOLV_FULLLOG") || getenv("ZYPP_LIBSAT_FULLLOG") )
00185           ::pool_setdebuglevel( _pool, 4 );
00186         else if ( getenv("ZYPP_FULLLOG") )
00187           ::pool_setdebuglevel( _pool, 2 );
00188         else
00189           ::pool_setdebugmask(_pool, SOLV_DEBUG_JOB|SOLV_DEBUG_STATS);
00190 
00191         ::pool_setdebugcallback( _pool, logSat, NULL );
00192 
00193         // set namespace callback
00194         _pool->nscallback = &nsCallback;
00195         _pool->nscallbackdata = (void*)this;
00196       }
00197 
00199       //
00200       //        METHOD NAME : PoolImpl::~PoolImpl
00201       //        METHOD TYPE : Dtor
00202       //
00203       PoolImpl::~PoolImpl()
00204       {
00205         ::pool_free( _pool );
00206       }
00207 
00209 
00210       void PoolImpl::setDirty( const char * a1, const char * a2, const char * a3 )
00211       {
00212         if ( a1 )
00213         {
00214           if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
00215           else if ( a2 ) MIL << a1 << " " << a2 << endl;
00216           else           MIL << a1 << endl;
00217         }
00218         _serial.setDirty();           // pool content change
00219         _availableLocalesPtr.reset(); // available locales may change
00220         _multiversionListPtr.reset(); // re-evaluate ZConfig::multiversionSpec.
00221 
00222         // invaldate dependency/namespace related indices:
00223         depSetDirty();
00224       }
00225 
00226       void PoolImpl::depSetDirty( const char * a1, const char * a2, const char * a3 )
00227       {
00228         if ( a1 )
00229         {
00230           if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
00231           else if ( a2 ) MIL << a1 << " " << a2 << endl;
00232           else           MIL << a1 << endl;
00233         }
00234         ::pool_freewhatprovides( _pool );
00235       }
00236 
00237       void PoolImpl::prepare() const
00238       {
00239         if ( _watcher.remember( _serial ) )
00240         {
00241           // After repo/solvable add/remove:
00242           // set pool architecture
00243           ::pool_setarch( _pool,  ZConfig::instance().systemArchitecture().asString().c_str() );
00244         }
00245         if ( ! _pool->whatprovides )
00246         {
00247           MIL << "pool_createwhatprovides..." << endl;
00248 
00249           ::pool_addfileprovides( _pool );
00250           ::pool_createwhatprovides( _pool );
00251         }
00252         if ( ! _pool->languages )
00253         {
00254           // initial seting
00255           const_cast<PoolImpl*>(this)->setTextLocale( ZConfig::instance().textLocale() );
00256         }
00257       }
00258 
00259       void PoolImpl::prepareForSolving() const
00260       {
00261         // additional /etc/sysconfig/storage check:
00262         static WatchFile sysconfigFile( sysconfigStoragePath(), WatchFile::NO_INIT );
00263         if ( sysconfigFile.hasChanged() )
00264         {
00265           _requiredFilesystemsPtr.reset(); // recreated on demand
00266           const_cast<PoolImpl*>(this)->depSetDirty( "/etc/sysconfig/storage change" );
00267         }
00268         // finally prepare as usual:
00269         prepare();
00270       }
00271 
00273 
00274       ::_Repo * PoolImpl::_createRepo( const std::string & name_r )
00275       {
00276         setDirty(__FUNCTION__, name_r.c_str() );
00277         ::_Repo * ret = ::repo_create( _pool, name_r.c_str() );
00278         if ( ret && name_r == systemRepoAlias() )
00279           ::pool_set_installed( _pool, ret );
00280         return ret;
00281       }
00282 
00283       void PoolImpl::_deleteRepo( ::_Repo * repo_r )
00284       {
00285         setDirty(__FUNCTION__, repo_r->name );
00286         ::repo_free( repo_r, /*reuseids*/false );
00287         eraseRepoInfo( repo_r );
00288         if ( isSystemRepo( repo_r ) )
00289         {
00290           // systemRepo added
00291           _onSystemByUserListPtr.reset(); // re-evaluate
00292         }
00293       }
00294 
00295       int PoolImpl::_addSolv( ::_Repo * repo_r, FILE * file_r )
00296       {
00297         setDirty(__FUNCTION__, repo_r->name );
00298         int ret = ::repo_add_solv( repo_r , file_r );
00299         if ( ret == 0 )
00300           _postRepoAdd( repo_r );
00301         return ret;
00302       }
00303 
00304       int PoolImpl::_addHelix( ::_Repo * repo_r, FILE * file_r )
00305       {
00306         setDirty(__FUNCTION__, repo_r->name );
00307         ::repo_add_helix( repo_r , file_r, 0 ); // unfortunately void
00308         _postRepoAdd( repo_r );
00309         return 0;
00310       }
00311 
00312       void PoolImpl::_postRepoAdd( ::_Repo * repo_r )
00313       {
00314         if ( ! isSystemRepo( repo_r ) )
00315         {
00316             // Filter out unwanted archs
00317           std::set<detail::IdType> sysids;
00318           {
00319             Arch::CompatSet sysarchs( Arch::compatSet( ZConfig::instance().systemArchitecture() ) );
00320             for_( it, sysarchs.begin(), sysarchs.end() )
00321               sysids.insert( it->id() );
00322 
00323               // unfortunately libsolv treats src/nosrc as architecture:
00324             sysids.insert( ARCH_SRC );
00325             sysids.insert( ARCH_NOSRC );
00326           }
00327 
00328           detail::IdType blockBegin = 0;
00329           unsigned       blockSize  = 0;
00330           for ( detail::IdType i = repo_r->start; i < repo_r->end; ++i )
00331           {
00332               ::_Solvable * s( _pool->solvables + i );
00333               if ( s->repo == repo_r && sysids.find( s->arch ) == sysids.end() )
00334               {
00335                 // Remember an unwanted arch entry:
00336                 if ( ! blockBegin )
00337                   blockBegin = i;
00338                 ++blockSize;
00339               }
00340               else if ( blockSize )
00341               {
00342                 // Free remembered entries
00343                   ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
00344                   blockBegin = blockSize = 0;
00345               }
00346           }
00347           if ( blockSize )
00348           {
00349               // Free remembered entries
00350               ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
00351               blockBegin = blockSize = 0;
00352           }
00353         }
00354         else
00355         {
00356           // systemRepo added
00357           _onSystemByUserListPtr.reset(); // re-evaluate
00358         }
00359       }
00360 
00361       detail::SolvableIdType PoolImpl::_addSolvables( ::_Repo * repo_r, unsigned count_r )
00362       {
00363         setDirty(__FUNCTION__, repo_r->name );
00364         return ::repo_add_solvable_block( repo_r, count_r );
00365       }
00366 
00367       void PoolImpl::setRepoInfo( RepoIdType id_r, const RepoInfo & info_r )
00368       {
00369         ::_Repo * repo( getRepo( id_r ) );
00370         if ( repo )
00371         {
00372           bool dirty = false;
00373 
00374           // libsolv priority is based on '<', while yum's repoinfo
00375           // uses 1(highest)->99(lowest). Thus we use -info_r.priority.
00376           if ( repo->priority != int(-info_r.priority()) )
00377           {
00378             repo->priority = -info_r.priority();
00379             dirty = true;
00380           }
00381 
00382           // subpriority is used to e.g. prefer http over dvd iff
00383           // both have same priority.
00384           int mediaPriority( media::MediaPriority( info_r.url() ) );
00385           if ( repo->subpriority != mediaPriority )
00386           {
00387             repo->subpriority = mediaPriority;
00388             dirty = true;
00389           }
00390 
00391           if ( dirty )
00392             setDirty(__FUNCTION__, info_r.alias().c_str() );
00393         }
00394         _repoinfos[id_r] = info_r;
00395       }
00396 
00398 
00399       // need on demand and id based Locale
00400       void _locale_hack( const LocaleSet & locales_r,
00401                          std::tr1::unordered_set<IdString> & locale2Solver )
00402       {
00403         std::tr1::unordered_set<IdString>( 2*locales_r.size() ).swap( locale2Solver );
00404         for_( it, locales_r.begin(),locales_r.end() )
00405         {
00406           for ( Locale l( *it ); l != Locale::noCode; l = l.fallback() )
00407             locale2Solver.insert( IdString( l.code() ) );
00408         }
00409         MIL << "New Solver Locales: " << locale2Solver << endl;
00410       }
00411 
00412       void PoolImpl::setTextLocale( const Locale & locale_r )
00413       {
00414         std::vector<std::string> fallbacklist;
00415         for ( Locale l( locale_r ); l != Locale::noCode; l = l.fallback() )
00416         {
00417           fallbacklist.push_back( l.code() );
00418         }
00419         dumpRangeLine( MIL << "pool_set_languages: ", fallbacklist.begin(), fallbacklist.end() ) << endl;
00420 
00421         std::vector<const char *> fallbacklist_cstr;
00422         for_( it, fallbacklist.begin(), fallbacklist.end() )
00423         {
00424           fallbacklist_cstr.push_back( it->c_str() );
00425         }
00426         ::pool_set_languages( _pool, &fallbacklist_cstr.front(), fallbacklist_cstr.size() );
00427       }
00428 
00429       void PoolImpl::setRequestedLocales( const LocaleSet & locales_r )
00430       {
00431         depSetDirty( "setRequestedLocales" );
00432         _requestedLocales = locales_r;
00433         MIL << "New RequestedLocales: " << locales_r << endl;
00434         _locale_hack( _requestedLocales, _locale2Solver );
00435       }
00436 
00437       bool PoolImpl::addRequestedLocale( const Locale & locale_r )
00438       {
00439         if ( _requestedLocales.insert( locale_r ).second )
00440         {
00441           depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
00442           _locale_hack( _requestedLocales, _locale2Solver );
00443           return true;
00444         }
00445         return false;
00446       }
00447 
00448       bool PoolImpl::eraseRequestedLocale( const Locale & locale_r )
00449       {
00450         if ( _requestedLocales.erase( locale_r ) )
00451         {
00452           depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
00453           _locale_hack( _requestedLocales, _locale2Solver );
00454           return true;
00455         }
00456         return false;
00457       }
00458 
00459       static void _getLocaleDeps( Capability cap_r, std::tr1::unordered_set<sat::detail::IdType> & store_r )
00460       {
00461         // Collect locales from any 'namespace:language(lang)' dependency
00462         CapDetail detail( cap_r );
00463         if ( detail.kind() == CapDetail::EXPRESSION )
00464         {
00465           switch ( detail.capRel() )
00466           {
00467             case CapDetail::CAP_AND:
00468             case CapDetail::CAP_OR:
00469               // expand
00470               _getLocaleDeps( detail.lhs(), store_r );
00471               _getLocaleDeps( detail.rhs(), store_r );
00472               break;
00473 
00474             case CapDetail::CAP_NAMESPACE:
00475               if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
00476               {
00477                 store_r.insert( detail.rhs().id() );
00478               }
00479               break;
00480 
00481             case CapDetail::REL_NONE:
00482             case CapDetail::CAP_WITH:
00483             case CapDetail::CAP_ARCH:
00484               break; // unwanted
00485           }
00486         }
00487       }
00488 
00489       const LocaleSet & PoolImpl::getAvailableLocales() const
00490       {
00491         if ( !_availableLocalesPtr )
00492         {
00493           // Collect any 'namespace:language(ja)' dependencies
00494           std::tr1::unordered_set<sat::detail::IdType> tmp;
00495           Pool pool( Pool::instance() );
00496           for_( it, pool.solvablesBegin(), pool.solvablesEnd() )
00497           {
00498             Capabilities cap( it->supplements() );
00499             for_( cit, cap.begin(), cap.end() )
00500             {
00501               _getLocaleDeps( *cit, tmp );
00502             }
00503           }
00504 #warning immediately build LocaleSet as soon as Loale is an Id based type
00505           _availableLocalesPtr.reset( new LocaleSet(tmp.size()) );
00506           for_( it, tmp.begin(), tmp.end() )
00507           {
00508             _availableLocalesPtr->insert( Locale( IdString(*it) ) );
00509           }
00510         }
00511         return *_availableLocalesPtr;
00512       }
00513 
00514       void PoolImpl::multiversionListInit() const
00515       {
00516         _multiversionListPtr.reset( new MultiversionList );
00517         MultiversionList & multiversionList( *_multiversionListPtr );
00518 
00519         const std::set<std::string> & multiversionSpec( ZConfig::instance().multiversionSpec() );
00520         for_( it, multiversionSpec.begin(), multiversionSpec.end() )
00521         {
00522           static const std::string prefix( "provides:" );
00523           if ( str::hasPrefix( *it, prefix ) )
00524           {
00525             WhatProvides provides( Capability( it->c_str() + prefix.size() ) );
00526             if ( provides.empty() )
00527             {
00528               MIL << "Multiversion install not provided (" << *it << ")" << endl;
00529             }
00530             else
00531             {
00532               for_( pit, provides.begin(), provides.end() )
00533               {
00534                 if ( multiversionList.insert( pit->ident() ).second )
00535                   MIL << "Multiversion install " << pit->ident() << " (" << *it << ")" << endl;
00536               }
00537             }
00538           }
00539           else
00540           {
00541             MIL << "Multiversion install " << *it << endl;
00542             multiversionList.insert( IdString( *it ) );
00543           }
00544         }
00545       }
00546 
00547       void PoolImpl::onSystemByUserListInit() const
00548       {
00549         _onSystemByUserListPtr.reset( new OnSystemByUserList );
00550         OnSystemByUserList & onSystemByUserList( *_onSystemByUserListPtr );
00551 
00552         Pathname root( ZConfig::instance().systemRoot() );
00553         if ( root.empty() )
00554         {
00555           MIL << "Target not initialized." << endl;
00556           return;
00557         }
00558         PathInfo pi( root / ZConfig::instance().historyLogFile() );
00559         MIL << "onSystemByUserList from history: " << pi << endl;
00560         if ( ! pi.isFile() )
00561           return;
00562 
00563         // go and parse it: 'who' must constain an '@', then it was installed by user request.
00564         // 2009-09-29 07:25:19|install|lirc-remotes|0.8.5-3.2|x86_64|root@opensuse|InstallationImage|a204211eb0...
00565         std::ifstream infile( pi.path().c_str() );
00566         for( iostr::EachLine in( infile ); in; in.next() )
00567         {
00568           const char * ch( (*in).c_str() );
00569           // start with year
00570           if ( *ch < '1' || '9' < *ch )
00571             continue;
00572           const char * sep1 = ::strchr( ch, '|' );      // | after date
00573           if ( !sep1 )
00574             continue;
00575           ++sep1;
00576           // if logs an install or delete
00577           bool installs = true;
00578           if ( ::strncmp( sep1, "install|", 8 ) )
00579           {
00580             if ( ::strncmp( sep1, "remove |", 8 ) )
00581               continue; // no install and no remove
00582               else
00583                 installs = false; // remove
00584           }
00585           sep1 += 8;                                    // | after what
00586           // get the package name
00587           const char * sep2 = ::strchr( sep1, '|' );    // | after name
00588           if ( !sep2 || sep1 == sep2 )
00589             continue;
00590           (*in)[sep2-ch] = '\0';
00591           IdString pkg( sep1 );
00592           // we're done, if a delete
00593           if ( !installs )
00594           {
00595             onSystemByUserList.erase( pkg );
00596             continue;
00597           }
00598           // now guess whether user installed or not (3rd next field contains 'user@host')
00599           if ( (sep1 = ::strchr( sep2+1, '|' ))         // | after version
00600             && (sep1 = ::strchr( sep1+1, '|' ))         // | after arch
00601             && (sep2 = ::strchr( sep1+1, '|' )) )       // | after who
00602           {
00603             (*in)[sep2-ch] = '\0';
00604             if ( ::strchr( sep1+1, '@' ) )
00605             {
00606               // by user
00607               onSystemByUserList.insert( pkg );
00608               continue;
00609             }
00610           }
00611         }
00612         MIL << "onSystemByUserList found: " << onSystemByUserList.size() << endl;
00613       }
00614 
00615       const std::set<std::string> & PoolImpl::requiredFilesystems() const
00616       {
00617         if ( ! _requiredFilesystemsPtr )
00618         {
00619           _requiredFilesystemsPtr.reset( new std::set<std::string> );
00620           std::set<std::string> & requiredFilesystems( *_requiredFilesystemsPtr );
00621           str::split( base::sysconfig::read( sysconfigStoragePath() )["USED_FS_LIST"],
00622                       std::inserter( requiredFilesystems, requiredFilesystems.end() ) );
00623         }
00624         return *_requiredFilesystemsPtr;
00625       }
00626 
00628     } // namespace detail
00631   } // namespace sat
00634 } // namespace zypp