libzypp 8.13.6

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