1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/sat/detail/PoolImpl.cc
14 #include <boost/mpl/int.hpp>
16 #include <zypp/base/Easy.h>
17 #include <zypp/base/LogTools.h>
18 #include <zypp/base/Gettext.h>
19 #include <zypp/base/Exception.h>
20 #include <zypp/base/Measure.h>
21 #include <zypp/base/WatchFile.h>
22 #include <zypp/base/Sysconfig.h>
23 #include <zypp/base/IOStream.h>
25 #include <zypp/ZConfig.h>
27 #include <zypp/sat/detail/PoolImpl.h>
28 #include <zypp/sat/SolvableSet.h>
29 #include <zypp/sat/Pool.h>
30 #include <zypp/Capability.h>
31 #include <zypp/Locale.h>
32 #include <zypp/PoolItem.h>
34 #include <zypp/target/modalias/Modalias.h>
35 #include <zypp/media/MediaPriority.h>
39 // Workaround libsolv project not providing a common include
40 // directory. (the -devel package does, but the git repo doesn't).
41 // #include <solv/repo_helix.h>
42 int repo_add_helix( ::Repo *repo, FILE *fp, int flags );
47 #undef ZYPP_BASE_LOGGER_LOGGROUP
48 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::satpool"
50 // ///////////////////////////////////////////////////////////////////
53 /////////////////////////////////////////////////////////////////
57 inline int LIBSOLV_DEBUGMASK()
59 const char * envp = getenv("LIBSOLV_DEBUGMASK");
60 return envp ? str::strtonum<int>( envp ) : 0;
63 ///////////////////////////////////////////////////////////////////
65 { /////////////////////////////////////////////////////////////////
67 ///////////////////////////////////////////////////////////////////
69 { /////////////////////////////////////////////////////////////////
71 // MPL checks for satlib constants we redefine to avoid
72 // includes and defines.
73 BOOST_MPL_ASSERT_RELATION( noId, ==, STRID_NULL );
74 BOOST_MPL_ASSERT_RELATION( emptyId, ==, STRID_EMPTY );
76 BOOST_MPL_ASSERT_RELATION( noSolvableId, ==, ID_NULL );
77 BOOST_MPL_ASSERT_RELATION( systemSolvableId, ==, SYSTEMSOLVABLE );
79 BOOST_MPL_ASSERT_RELATION( solvablePrereqMarker, ==, SOLVABLE_PREREQMARKER );
80 BOOST_MPL_ASSERT_RELATION( solvableFileMarker, ==, SOLVABLE_FILEMARKER );
82 BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_AND, ==, REL_AND );
83 BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_OR, ==, REL_OR );
84 BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_WITH, ==, REL_WITH );
85 BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_NAMESPACE, ==, REL_NAMESPACE );
86 BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_ARCH, ==, REL_ARCH );
88 BOOST_MPL_ASSERT_RELATION( namespaceModalias, ==, NAMESPACE_MODALIAS );
89 BOOST_MPL_ASSERT_RELATION( namespaceLanguage, ==, NAMESPACE_LANGUAGE );
90 BOOST_MPL_ASSERT_RELATION( namespaceFilesystem, ==, NAMESPACE_FILESYSTEM );
92 /////////////////////////////////////////////////////////////////
94 const std::string & PoolImpl::systemRepoAlias()
96 static const std::string _val( "@System" );
100 const Pathname & sysconfigStoragePath()
102 static const Pathname _val( "/etc/sysconfig/storage" );
106 /////////////////////////////////////////////////////////////////
108 static void logSat( CPool *, void *data, int type, const char *logString )
110 // "1234567890123456789012345678901234567890
111 if ( 0 == strncmp( logString, "job: user installed", 19 ) )
113 if ( 0 == strncmp( logString, "job: multiversion", 17 ) )
115 if ( 0 == strncmp( logString, " - no rule created", 19 ) )
117 if ( 0 == strncmp( logString, " next rules: 0 0", 19 ) )
120 if ( type & (SOLV_FATAL|SOLV_ERROR) ) {
121 L_ERR("libsolv") << logString;
122 } else if ( type & SOLV_DEBUG_STATS ) {
123 L_DBG("libsolv") << logString;
125 L_MIL("libsolv") << logString;
129 detail::IdType PoolImpl::nsCallback( CPool *, void * data, detail::IdType lhs, detail::IdType rhs )
131 // lhs: the namespace identifier, e.g. NAMESPACE:MODALIAS
132 // rhs: the value, e.g. pci:v0000104Cd0000840[01]sv*sd*bc*sc*i*
133 // return: 0 if not supportded
134 // 1 if supported by the system
135 // -1 AFAIK it's also possible to return a list of solvables that support it, but don't know how.
137 static const detail::IdType RET_unsupported = 0;
138 static const detail::IdType RET_systemProperty = 1;
141 case NAMESPACE_LANGUAGE:
143 const TrackedLocaleIds & localeIds( reinterpret_cast<PoolImpl*>(data)->trackedLocaleIds() );
144 return localeIds.contains( IdString(rhs) ) ? RET_systemProperty : RET_unsupported;
148 case NAMESPACE_MODALIAS:
150 // modalias strings in capability may be hexencoded because rpm does not allow
151 // ',', ' ' or other special chars.
152 return target::Modalias::instance().query( str::hexdecode( IdString(rhs).c_str() ) )
158 case NAMESPACE_FILESYSTEM:
160 const std::set<std::string> & requiredFilesystems( reinterpret_cast<PoolImpl*>(data)->requiredFilesystems() );
161 return requiredFilesystems.find( IdString(rhs).asString() ) != requiredFilesystems.end() ? RET_systemProperty : RET_unsupported;
167 WAR << "Unhandled " << Capability( lhs ) << " vs. " << Capability( rhs ) << endl;
168 return RET_unsupported;
171 ///////////////////////////////////////////////////////////////////
173 // METHOD NAME : PoolMember::myPool
174 // METHOD TYPE : PoolImpl
176 PoolImpl & PoolMember::myPool()
178 static PoolImpl _global;
182 ///////////////////////////////////////////////////////////////////
184 // METHOD NAME : PoolImpl::PoolImpl
185 // METHOD TYPE : Ctor
188 : _pool( ::pool_create() )
190 MIL << "Creating sat-pool." << endl;
193 ZYPP_THROW( Exception( _("Can not create sat-pool.") ) );
195 // by now we support only a RPM backend
196 ::pool_setdisttype(_pool, DISTTYPE_RPM );
198 // initialialize logging
199 if ( env::LIBSOLV_DEBUGMASK() )
201 ::pool_setdebugmask(_pool, env::LIBSOLV_DEBUGMASK() );
205 if ( getenv("ZYPP_LIBSOLV_FULLLOG") || getenv("ZYPP_LIBSAT_FULLLOG") )
206 ::pool_setdebuglevel( _pool, 3 );
207 else if ( getenv("ZYPP_FULLLOG") )
208 ::pool_setdebuglevel( _pool, 2 );
210 ::pool_setdebugmask(_pool, SOLV_DEBUG_JOB|SOLV_DEBUG_STATS );
213 ::pool_setdebugcallback( _pool, logSat, NULL );
215 // set namespace callback
216 _pool->nscallback = &nsCallback;
217 _pool->nscallbackdata = (void*)this;
220 ///////////////////////////////////////////////////////////////////
222 // METHOD NAME : PoolImpl::~PoolImpl
223 // METHOD TYPE : Dtor
225 PoolImpl::~PoolImpl()
227 ::pool_free( _pool );
230 ///////////////////////////////////////////////////////////////////
232 void PoolImpl::setDirty( const char * a1, const char * a2, const char * a3 )
236 if ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
237 else if ( a2 ) MIL << a1 << " " << a2 << endl;
238 else MIL << a1 << endl;
240 _serial.setDirty(); // pool content change
241 _availableLocalesPtr.reset(); // available locales may change
242 _multiversionListPtr.reset(); // re-evaluate ZConfig::multiversionSpec.
243 _needrebootSpec.setDirty(); // re-evaluate needrebootSpec
245 depSetDirty(); // invaldate dependency/namespace related indices
248 void PoolImpl::localeSetDirty( const char * a1, const char * a2, const char * a3 )
252 if ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
253 else if ( a2 ) MIL << a1 << " " << a2 << endl;
254 else MIL << a1 << endl;
256 _trackedLocaleIdsPtr.reset(); // requested locales changed
257 depSetDirty(); // invaldate dependency/namespace related indices
260 void PoolImpl::depSetDirty( const char * a1, const char * a2, const char * a3 )
264 if ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
265 else if ( a2 ) MIL << a1 << " " << a2 << endl;
266 else MIL << a1 << endl;
268 ::pool_freewhatprovides( _pool );
271 void PoolImpl::prepare() const
273 // additional /etc/sysconfig/storage check:
274 static WatchFile sysconfigFile( sysconfigStoragePath(), WatchFile::NO_INIT );
275 if ( sysconfigFile.hasChanged() )
277 _requiredFilesystemsPtr.reset(); // recreated on demand
278 const_cast<PoolImpl*>(this)->depSetDirty( "/etc/sysconfig/storage change" );
280 if ( _watcher.remember( _serial ) )
282 // After repo/solvable add/remove:
283 // set pool architecture
284 ::pool_setarch( _pool, ZConfig::instance().systemArchitecture().asString().c_str() );
286 if ( ! _pool->whatprovides )
288 MIL << "pool_createwhatprovides..." << endl;
290 ::pool_addfileprovides( _pool );
291 ::pool_createwhatprovides( _pool );
293 if ( ! _pool->languages )
296 const_cast<PoolImpl*>(this)->setTextLocale( ZConfig::instance().textLocale() );
300 ///////////////////////////////////////////////////////////////////
302 CRepo * PoolImpl::_createRepo( const std::string & name_r )
304 setDirty(__FUNCTION__, name_r.c_str() );
305 CRepo * ret = ::repo_create( _pool, name_r.c_str() );
306 if ( ret && name_r == systemRepoAlias() )
307 ::pool_set_installed( _pool, ret );
311 void PoolImpl::_deleteRepo( CRepo * repo_r )
313 setDirty(__FUNCTION__, repo_r->name );
314 if ( isSystemRepo( repo_r ) )
315 _autoinstalled.clear();
316 eraseRepoInfo( repo_r );
317 ::repo_free( repo_r, /*resusePoolIDs*/false );
318 // If the last repo is removed clear the pool to actually reuse all IDs.
319 // NOTE: the explicit ::repo_free above asserts all solvables are memset(0)!
320 if ( !_pool->urepos )
322 _serialIDs.setDirty(); // Indicate resusePoolIDs - ResPool must also invalidate it's PoolItems
323 ::pool_freeallrepos( _pool, /*resusePoolIDs*/true );
327 int PoolImpl::_addSolv( CRepo * repo_r, FILE * file_r )
329 setDirty(__FUNCTION__, repo_r->name );
330 int ret = ::repo_add_solv( repo_r, file_r, 0 );
332 _postRepoAdd( repo_r );
336 int PoolImpl::_addHelix( CRepo * repo_r, FILE * file_r )
338 setDirty(__FUNCTION__, repo_r->name );
339 int ret = ::repo_add_helix( repo_r, file_r, 0 );
341 _postRepoAdd( repo_r );
345 void PoolImpl::_postRepoAdd( CRepo * repo_r )
347 if ( ! isSystemRepo( repo_r ) )
349 // Filter out unwanted archs
350 std::set<detail::IdType> sysids;
352 Arch::CompatSet sysarchs( Arch::compatSet( ZConfig::instance().systemArchitecture() ) );
353 for_( it, sysarchs.begin(), sysarchs.end() )
354 sysids.insert( it->id() );
356 // unfortunately libsolv treats src/nosrc as architecture:
357 sysids.insert( ARCH_SRC );
358 sysids.insert( ARCH_NOSRC );
361 detail::IdType blockBegin = 0;
362 unsigned blockSize = 0;
363 for ( detail::IdType i = repo_r->start; i < repo_r->end; ++i )
365 CSolvable * s( _pool->solvables + i );
366 if ( s->repo == repo_r && sysids.find( s->arch ) == sysids.end() )
368 // Remember an unwanted arch entry:
373 else if ( blockSize )
375 // Free remembered entries
376 ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*resusePoolIDs*/false );
377 blockBegin = blockSize = 0;
382 // Free remembered entries
383 ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*resusePoolIDs*/false );
384 blockBegin = blockSize = 0;
389 detail::SolvableIdType PoolImpl::_addSolvables( CRepo * repo_r, unsigned count_r )
391 setDirty(__FUNCTION__, repo_r->name );
392 return ::repo_add_solvable_block( repo_r, count_r );
395 void PoolImpl::setRepoInfo( RepoIdType id_r, const RepoInfo & info_r )
397 CRepo * repo( getRepo( id_r ) );
402 // libsolv priority is based on '<', while yum's repoinfo
403 // uses 1(highest)->99(lowest). Thus we use -info_r.priority.
404 if ( repo->priority != int(-info_r.priority()) )
406 repo->priority = -info_r.priority();
410 // subpriority is used to e.g. prefer http over dvd iff
411 // both have same priority.
412 int mediaPriority( media::MediaPriority( info_r.url() ) );
413 if ( repo->subpriority != mediaPriority )
415 repo->subpriority = mediaPriority;
420 setDirty(__FUNCTION__, info_r.alias().c_str() );
422 _repoinfos[id_r] = info_r;
425 ///////////////////////////////////////////////////////////////////
427 void PoolImpl::setTextLocale( const Locale & locale_r )
431 // We need one, so "en" is the last resort
432 const char *needone[] { "en" };
433 ::pool_set_languages( _pool, needone, 1 );
437 std::vector<std::string> fallbacklist;
438 for ( Locale l( locale_r ); l; l = l.fallback() )
440 fallbacklist.push_back( l.code() );
442 dumpRangeLine( MIL << "pool_set_languages: ", fallbacklist.begin(), fallbacklist.end() ) << endl;
444 std::vector<const char *> fallbacklist_cstr;
445 for_( it, fallbacklist.begin(), fallbacklist.end() )
447 fallbacklist_cstr.push_back( it->c_str() );
449 ::pool_set_languages( _pool, &fallbacklist_cstr.front(), fallbacklist_cstr.size() );
452 void PoolImpl::initRequestedLocales( const LocaleSet & locales_r )
454 if ( _requestedLocalesTracker.setInitial( locales_r ) )
456 localeSetDirty( "initRequestedLocales" );
457 MIL << "Init RequestedLocales: " << _requestedLocalesTracker << " =" << locales_r << endl;
461 void PoolImpl::setRequestedLocales( const LocaleSet & locales_r )
463 if ( _requestedLocalesTracker.set( locales_r ) )
465 localeSetDirty( "setRequestedLocales" );
466 MIL << "New RequestedLocales: " << _requestedLocalesTracker << " =" << locales_r << endl;
470 bool PoolImpl::addRequestedLocale( const Locale & locale_r )
472 bool done = _requestedLocalesTracker.add( locale_r );
475 localeSetDirty( "addRequestedLocale", locale_r.code().c_str() );
476 MIL << "New RequestedLocales: " << _requestedLocalesTracker << " +" << locale_r << endl;
481 bool PoolImpl::eraseRequestedLocale( const Locale & locale_r )
483 bool done = _requestedLocalesTracker.remove( locale_r );
486 localeSetDirty( "addRequestedLocale", locale_r.code().c_str() );
487 MIL << "New RequestedLocales: " << _requestedLocalesTracker << " -" << locale_r << endl;
493 const PoolImpl::TrackedLocaleIds & PoolImpl::trackedLocaleIds() const
495 if ( ! _trackedLocaleIdsPtr )
497 _trackedLocaleIdsPtr.reset( new TrackedLocaleIds );
499 const base::SetTracker<LocaleSet> & localesTracker( _requestedLocalesTracker );
500 TrackedLocaleIds & localeIds( *_trackedLocaleIdsPtr );
502 // Add current locales+fallback except for added ones
503 for ( Locale lang: localesTracker.current() )
505 if ( localesTracker.wasAdded( lang ) )
507 for ( ; lang; lang = lang.fallback() )
508 { localeIds.current().insert( IdString(lang) ); }
511 // Add added locales+fallback except they are already in current
512 for ( Locale lang: localesTracker.added() )
514 for ( ; lang && localeIds.current().insert( IdString(lang) ).second; lang = lang.fallback() )
515 { localeIds.added().insert( IdString(lang) ); }
518 // Add removed locales+fallback except they are still in current
519 for ( Locale lang: localesTracker.removed() )
521 for ( ; lang && ! localeIds.current().count( IdString(lang) ); lang = lang.fallback() )
522 { localeIds.removed().insert( IdString(lang) ); }
525 // bsc#1155678: We try to differ between an empty RequestedLocales
526 // and one containing 'en' (explicit or as fallback). An empty RequestedLocales
527 // should not even drag in recommended 'en' packages. So we no longer enforce
528 // 'en' being in the set.
530 return *_trackedLocaleIdsPtr;
534 static void _getLocaleDeps( const Capability & cap_r, LocaleSet & store_r )
536 // Collect locales from any 'namespace:language(lang)' dependency
537 CapDetail detail( cap_r );
538 if ( detail.kind() == CapDetail::EXPRESSION )
540 switch ( detail.capRel() )
542 case CapDetail::CAP_AND:
543 case CapDetail::CAP_OR:
545 _getLocaleDeps( detail.lhs(), store_r );
546 _getLocaleDeps( detail.rhs(), store_r );
549 case CapDetail::CAP_NAMESPACE:
550 if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
552 store_r.insert( Locale( IdString(detail.rhs().id()) ) );
556 case CapDetail::REL_NONE:
557 case CapDetail::CAP_WITH:
558 case CapDetail::CAP_ARCH:
564 const LocaleSet & PoolImpl::getAvailableLocales() const
566 if ( !_availableLocalesPtr )
568 _availableLocalesPtr.reset( new LocaleSet );
569 LocaleSet & localeSet( *_availableLocalesPtr );
571 for ( const Solvable & pi : Pool::instance().solvables() )
573 for ( const Capability & cap : pi.supplements() )
575 _getLocaleDeps( cap, localeSet );
579 return *_availableLocalesPtr;
582 ///////////////////////////////////////////////////////////////////
584 void PoolImpl::multiversionListInit() const
586 _multiversionListPtr.reset( new MultiversionList );
587 MultiversionList & multiversionList( *_multiversionListPtr );
589 MultiversionList::size_type size = 0;
590 for ( const std::string & spec : ZConfig::instance().multiversionSpec() )
592 static const std::string prefix( "provides:" );
593 bool provides = str::hasPrefix( spec, prefix );
595 for ( Solvable solv : WhatProvides( Capability( provides ? spec.c_str() + prefix.size() : spec.c_str() ) ) )
597 if ( provides || solv.ident() == spec )
598 multiversionList.insert( solv );
601 MultiversionList::size_type nsize = multiversionList.size();
602 MIL << "Multiversion install " << spec << ": " << (nsize-size) << " matches" << endl;
607 void PoolImpl::multiversionSpecChanged()
608 { _multiversionListPtr.reset(); }
610 const PoolImpl::MultiversionList & PoolImpl::multiversionList() const
612 if ( ! _multiversionListPtr )
613 multiversionListInit();
614 return *_multiversionListPtr;
617 bool PoolImpl::isMultiversion( const Solvable & solv_r ) const
618 { return multiversionList().contains( solv_r ); }
620 ///////////////////////////////////////////////////////////////////
622 const std::set<std::string> & PoolImpl::requiredFilesystems() const
624 if ( ! _requiredFilesystemsPtr )
626 _requiredFilesystemsPtr.reset( new std::set<std::string> );
627 std::set<std::string> & requiredFilesystems( *_requiredFilesystemsPtr );
628 str::split( base::sysconfig::read( sysconfigStoragePath() )["USED_FS_LIST"],
629 std::inserter( requiredFilesystems, requiredFilesystems.end() ) );
631 return *_requiredFilesystemsPtr;
634 /////////////////////////////////////////////////////////////////
635 } // namespace detail
636 ///////////////////////////////////////////////////////////////////
637 /////////////////////////////////////////////////////////////////
639 ///////////////////////////////////////////////////////////////////
640 /////////////////////////////////////////////////////////////////
642 ///////////////////////////////////////////////////////////////////