libzypp  17.14.0
PoolImpl.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include <iostream>
13 #include <fstream>
14 #include <boost/mpl/int.hpp>
15 
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"
24 
25 #include "zypp/ZConfig.h"
26 
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"
33 
36 
37 extern "C"
38 {
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 );
43 }
44 
45 using std::endl;
46 
47 #undef ZYPP_BASE_LOGGER_LOGGROUP
48 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::satpool"
49 
50 // ///////////////////////////////////////////////////////////////////
51 namespace zypp
52 {
54  namespace env
55  {
57  inline int LIBSOLV_DEBUGMASK()
58  {
59  const char * envp = getenv("LIBSOLV_DEBUGMASK");
60  return envp ? str::strtonum<int>( envp ) : 0;
61  }
62  } // namespace env
64  namespace sat
65  {
66 
68  namespace detail
69  {
70 
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 );
75 
77  BOOST_MPL_ASSERT_RELATION( systemSolvableId, ==, SYSTEMSOLVABLE );
78 
79  BOOST_MPL_ASSERT_RELATION( solvablePrereqMarker, ==, SOLVABLE_PREREQMARKER );
80  BOOST_MPL_ASSERT_RELATION( solvableFileMarker, ==, SOLVABLE_FILEMARKER );
81 
87 
88  BOOST_MPL_ASSERT_RELATION( namespaceModalias, ==, NAMESPACE_MODALIAS );
89  BOOST_MPL_ASSERT_RELATION( namespaceLanguage, ==, NAMESPACE_LANGUAGE );
90  BOOST_MPL_ASSERT_RELATION( namespaceFilesystem, ==, NAMESPACE_FILESYSTEM );
91 
93 
94  const std::string & PoolImpl::systemRepoAlias()
95  {
96  static const std::string _val( "@System" );
97  return _val;
98  }
99 
100  const Pathname & sysconfigStoragePath()
101  {
102  static const Pathname _val( "/etc/sysconfig/storage" );
103  return _val;
104  }
105 
107 
108  static void logSat( CPool *, void *data, int type, const char *logString )
109  {
110  // "1234567890123456789012345678901234567890
111  if ( 0 == strncmp( logString, "job: user installed", 19 ) )
112  return;
113  if ( 0 == strncmp( logString, "job: multiversion", 17 ) )
114  return;
115  if ( 0 == strncmp( logString, " - no rule created", 19 ) )
116  return;
117  if ( 0 == strncmp( logString, " next rules: 0 0", 19 ) )
118  return;
119 
120  if ( type & (SOLV_FATAL|SOLV_ERROR) ) {
121  L_ERR("libsolv") << logString;
122  } else if ( type & SOLV_DEBUG_STATS ) {
123  L_DBG("libsolv") << logString;
124  } else {
125  L_MIL("libsolv") << logString;
126  }
127  }
128 
130  {
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.
136 
137  static const detail::IdType RET_unsupported = 0;
138  static const detail::IdType RET_systemProperty = 1;
139  switch ( lhs )
140  {
141  case NAMESPACE_LANGUAGE:
142  {
143  const TrackedLocaleIds & localeIds( reinterpret_cast<PoolImpl*>(data)->trackedLocaleIds() );
144  return localeIds.contains( IdString(rhs) ) ? RET_systemProperty : RET_unsupported;
145  }
146  break;
147 
148  case NAMESPACE_MODALIAS:
149  {
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() ) )
153  ? RET_systemProperty
154  : RET_unsupported;
155  }
156  break;
157 
158  case NAMESPACE_FILESYSTEM:
159  {
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;
162  }
163  break;
164 
165  }
166 
167  WAR << "Unhandled " << Capability( lhs ) << " vs. " << Capability( rhs ) << endl;
168  return RET_unsupported;
169  }
170 
172  //
173  // METHOD NAME : PoolMember::myPool
174  // METHOD TYPE : PoolImpl
175  //
177  {
178  static PoolImpl _global;
179  return _global;
180  }
181 
183  //
184  // METHOD NAME : PoolImpl::PoolImpl
185  // METHOD TYPE : Ctor
186  //
188  : _pool( ::pool_create() )
189  {
190  MIL << "Creating sat-pool." << endl;
191  if ( ! _pool )
192  {
193  ZYPP_THROW( Exception( _("Can not create sat-pool.") ) );
194  }
195  // by now we support only a RPM backend
196  ::pool_setdisttype(_pool, DISTTYPE_RPM );
197 
198  // initialialize logging
199  if ( env::LIBSOLV_DEBUGMASK() )
200  {
201  ::pool_setdebugmask(_pool, env::LIBSOLV_DEBUGMASK() );
202  }
203  else
204  {
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 );
209  else
210  ::pool_setdebugmask(_pool, SOLV_DEBUG_JOB|SOLV_DEBUG_STATS );
211  }
212 
213  ::pool_setdebugcallback( _pool, logSat, NULL );
214 
215  // set namespace callback
216  _pool->nscallback = &nsCallback;
217  _pool->nscallbackdata = (void*)this;
218  }
219 
221  //
222  // METHOD NAME : PoolImpl::~PoolImpl
223  // METHOD TYPE : Dtor
224  //
226  {
227  ::pool_free( _pool );
228  }
229 
231 
232  void PoolImpl::setDirty( const char * a1, const char * a2, const char * a3 )
233  {
234  if ( a1 )
235  {
236  if ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
237  else if ( a2 ) MIL << a1 << " " << a2 << endl;
238  else MIL << a1 << endl;
239  }
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
244 
245  depSetDirty(); // invaldate dependency/namespace related indices
246  }
247 
248  void PoolImpl::localeSetDirty( const char * a1, const char * a2, const char * a3 )
249  {
250  if ( a1 )
251  {
252  if ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
253  else if ( a2 ) MIL << a1 << " " << a2 << endl;
254  else MIL << a1 << endl;
255  }
256  _trackedLocaleIdsPtr.reset(); // requested locales changed
257  depSetDirty(); // invaldate dependency/namespace related indices
258  }
259 
260  void PoolImpl::depSetDirty( const char * a1, const char * a2, const char * a3 )
261  {
262  if ( a1 )
263  {
264  if ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
265  else if ( a2 ) MIL << a1 << " " << a2 << endl;
266  else MIL << a1 << endl;
267  }
268  ::pool_freewhatprovides( _pool );
269  }
270 
271  void PoolImpl::prepare() const
272  {
273  // additional /etc/sysconfig/storage check:
274  static WatchFile sysconfigFile( sysconfigStoragePath(), WatchFile::NO_INIT );
275  if ( sysconfigFile.hasChanged() )
276  {
277  _requiredFilesystemsPtr.reset(); // recreated on demand
278  const_cast<PoolImpl*>(this)->depSetDirty( "/etc/sysconfig/storage change" );
279  }
280  if ( _watcher.remember( _serial ) )
281  {
282  // After repo/solvable add/remove:
283  // set pool architecture
284  ::pool_setarch( _pool, ZConfig::instance().systemArchitecture().asString().c_str() );
285  }
286  if ( ! _pool->whatprovides )
287  {
288  MIL << "pool_createwhatprovides..." << endl;
289 
290  ::pool_addfileprovides( _pool );
291  ::pool_createwhatprovides( _pool );
292  }
293  if ( ! _pool->languages )
294  {
295  // initial seting
296  const_cast<PoolImpl*>(this)->setTextLocale( ZConfig::instance().textLocale() );
297  }
298  }
299 
301 
302  CRepo * PoolImpl::_createRepo( const std::string & name_r )
303  {
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 );
308  return ret;
309  }
310 
311  void PoolImpl::_deleteRepo( CRepo * repo_r )
312  {
313  setDirty(__FUNCTION__, repo_r->name );
314  if ( isSystemRepo( repo_r ) )
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 )
321  {
322  _serialIDs.setDirty(); // Indicate resusePoolIDs - ResPool must also invalidate it's PoolItems
323  ::pool_freeallrepos( _pool, /*resusePoolIDs*/true );
324  }
325  }
326 
327  int PoolImpl::_addSolv( CRepo * repo_r, FILE * file_r )
328  {
329  setDirty(__FUNCTION__, repo_r->name );
330  int ret = ::repo_add_solv( repo_r, file_r, 0 );
331  if ( ret == 0 )
332  _postRepoAdd( repo_r );
333  return ret;
334  }
335 
336  int PoolImpl::_addHelix( CRepo * repo_r, FILE * file_r )
337  {
338  setDirty(__FUNCTION__, repo_r->name );
339  int ret = ::repo_add_helix( repo_r, file_r, 0 );
340  if ( ret == 0 )
341  _postRepoAdd( repo_r );
342  return 0;
343  }
344 
345  void PoolImpl::_postRepoAdd( CRepo * repo_r )
346  {
347  if ( ! isSystemRepo( repo_r ) )
348  {
349  // Filter out unwanted archs
350  std::set<detail::IdType> sysids;
351  {
352  Arch::CompatSet sysarchs( Arch::compatSet( ZConfig::instance().systemArchitecture() ) );
353  for_( it, sysarchs.begin(), sysarchs.end() )
354  sysids.insert( it->id() );
355 
356  // unfortunately libsolv treats src/nosrc as architecture:
357  sysids.insert( ARCH_SRC );
358  sysids.insert( ARCH_NOSRC );
359  }
360 
361  detail::IdType blockBegin = 0;
362  unsigned blockSize = 0;
363  for ( detail::IdType i = repo_r->start; i < repo_r->end; ++i )
364  {
365  CSolvable * s( _pool->solvables + i );
366  if ( s->repo == repo_r && sysids.find( s->arch ) == sysids.end() )
367  {
368  // Remember an unwanted arch entry:
369  if ( ! blockBegin )
370  blockBegin = i;
371  ++blockSize;
372  }
373  else if ( blockSize )
374  {
375  // Free remembered entries
376  ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*resusePoolIDs*/false );
377  blockBegin = blockSize = 0;
378  }
379  }
380  if ( blockSize )
381  {
382  // Free remembered entries
383  ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*resusePoolIDs*/false );
384  blockBegin = blockSize = 0;
385  }
386  }
387  }
388 
390  {
391  setDirty(__FUNCTION__, repo_r->name );
392  return ::repo_add_solvable_block( repo_r, count_r );
393  }
394 
395  void PoolImpl::setRepoInfo( RepoIdType id_r, const RepoInfo & info_r )
396  {
397  CRepo * repo( getRepo( id_r ) );
398  if ( repo )
399  {
400  bool dirty = false;
401 
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()) )
405  {
406  repo->priority = -info_r.priority();
407  dirty = true;
408  }
409 
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 )
414  {
415  repo->subpriority = mediaPriority;
416  dirty = true;
417  }
418 
419  if ( dirty )
420  setDirty(__FUNCTION__, info_r.alias().c_str() );
421  }
422  _repoinfos[id_r] = info_r;
423  }
424 
426 
427  void PoolImpl::setTextLocale( const Locale & locale_r )
428  {
429  if ( ! locale_r )
430  {
431  // We need one, so "en" is the last resort
432  const char *needone[] { "en" };
433  ::pool_set_languages( _pool, needone, 1 );
434  return;
435  }
436 
437  std::vector<std::string> fallbacklist;
438  for ( Locale l( locale_r ); l; l = l.fallback() )
439  {
440  fallbacklist.push_back( l.code() );
441  }
442  dumpRangeLine( MIL << "pool_set_languages: ", fallbacklist.begin(), fallbacklist.end() ) << endl;
443 
444  std::vector<const char *> fallbacklist_cstr;
445  for_( it, fallbacklist.begin(), fallbacklist.end() )
446  {
447  fallbacklist_cstr.push_back( it->c_str() );
448  }
449  ::pool_set_languages( _pool, &fallbacklist_cstr.front(), fallbacklist_cstr.size() );
450  }
451 
452  void PoolImpl::initRequestedLocales( const LocaleSet & locales_r )
453  {
454  if ( _requestedLocalesTracker.setInitial( locales_r ) )
455  {
456  localeSetDirty( "initRequestedLocales" );
457  MIL << "Init RequestedLocales: " << _requestedLocalesTracker << " =" << locales_r << endl;
458  }
459  }
460 
461  void PoolImpl::setRequestedLocales( const LocaleSet & locales_r )
462  {
463  if ( _requestedLocalesTracker.set( locales_r ) )
464  {
465  localeSetDirty( "setRequestedLocales" );
466  MIL << "New RequestedLocales: " << _requestedLocalesTracker << " =" << locales_r << endl;
467  }
468  }
469 
470  bool PoolImpl::addRequestedLocale( const Locale & locale_r )
471  {
472  bool done = _requestedLocalesTracker.add( locale_r );
473  if ( done )
474  {
475  localeSetDirty( "addRequestedLocale", locale_r.code().c_str() );
476  MIL << "New RequestedLocales: " << _requestedLocalesTracker << " +" << locale_r << endl;
477  }
478  return done;
479  }
480 
481  bool PoolImpl::eraseRequestedLocale( const Locale & locale_r )
482  {
483  bool done = _requestedLocalesTracker.remove( locale_r );
484  if ( done )
485  {
486  localeSetDirty( "addRequestedLocale", locale_r.code().c_str() );
487  MIL << "New RequestedLocales: " << _requestedLocalesTracker << " -" << locale_r << endl;
488  }
489  return done;
490  }
491 
492 
494  {
495  if ( ! _trackedLocaleIdsPtr )
496  {
498 
499  const base::SetTracker<LocaleSet> & localesTracker( _requestedLocalesTracker );
500  TrackedLocaleIds & localeIds( *_trackedLocaleIdsPtr );
501 
502  // Add current locales+fallback except for added ones
503  for ( Locale lang: localesTracker.current() )
504  {
505  if ( localesTracker.wasAdded( lang ) )
506  continue;
507  for ( ; lang; lang = lang.fallback() )
508  { localeIds.current().insert( IdString(lang) ); }
509  }
510 
511  // Add added locales+fallback except they are already in current
512  for ( Locale lang: localesTracker.added() )
513  {
514  for ( ; lang && localeIds.current().insert( IdString(lang) ).second; lang = lang.fallback() )
515  { localeIds.added().insert( IdString(lang) ); }
516  }
517 
518  // Add removed locales+fallback except they are still in current
519  for ( Locale lang: localesTracker.removed() )
520  {
521  for ( ; lang && ! localeIds.current().count( IdString(lang) ); lang = lang.fallback() )
522  { localeIds.removed().insert( IdString(lang) ); }
523  }
524 
525  // Assert that TrackedLocaleIds::current is not empty.
526  // If, so fill in LanguageCode::enCode as last resort.
527  if ( localeIds.current().empty() )
528  { localeIds.current().insert( IdString(Locale::enCode) ); }
529  }
530  return *_trackedLocaleIdsPtr;
531  }
532 
533 
534  static void _getLocaleDeps( const Capability & cap_r, LocaleSet & store_r )
535  {
536  // Collect locales from any 'namespace:language(lang)' dependency
537  CapDetail detail( cap_r );
538  if ( detail.kind() == CapDetail::EXPRESSION )
539  {
540  switch ( detail.capRel() )
541  {
542  case CapDetail::CAP_AND:
543  case CapDetail::CAP_OR:
544  // expand
545  _getLocaleDeps( detail.lhs(), store_r );
546  _getLocaleDeps( detail.rhs(), store_r );
547  break;
548 
550  if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
551  {
552  store_r.insert( Locale( IdString(detail.rhs().id()) ) );
553  }
554  break;
555 
556  case CapDetail::REL_NONE:
557  case CapDetail::CAP_WITH:
558  case CapDetail::CAP_ARCH:
559  break; // unwanted
560  }
561  }
562  }
563 
565  {
566  if ( !_availableLocalesPtr )
567  {
568  _availableLocalesPtr.reset( new LocaleSet );
569  LocaleSet & localeSet( *_availableLocalesPtr );
570 
571  for ( const Solvable & pi : Pool::instance().solvables() )
572  {
573  for ( const Capability & cap : pi.supplements() )
574  {
575  _getLocaleDeps( cap, localeSet );
576  }
577  }
578  }
579  return *_availableLocalesPtr;
580  }
581 
583 
585  {
588 
590  for ( const std::string & spec : ZConfig::instance().multiversionSpec() )
591  {
592  static const std::string prefix( "provides:" );
593  bool provides = str::hasPrefix( spec, prefix );
594 
595  for ( Solvable solv : WhatProvides( Capability( provides ? spec.c_str() + prefix.size() : spec.c_str() ) ) )
596  {
597  if ( provides || solv.ident() == spec )
598  multiversionList.insert( solv );
599  }
600 
601  MultiversionList::size_type nsize = multiversionList.size();
602  MIL << "Multiversion install " << spec << ": " << (nsize-size) << " matches" << endl;
603  size = nsize;
604  }
605  }
606 
608  { _multiversionListPtr.reset(); }
609 
611  {
612  if ( ! _multiversionListPtr )
614  return *_multiversionListPtr;
615  }
616 
617  bool PoolImpl::isMultiversion( const Solvable & solv_r ) const
618  { return multiversionList().contains( solv_r ); }
619 
621 
622  const std::set<std::string> & PoolImpl::requiredFilesystems() const
623  {
624  if ( ! _requiredFilesystemsPtr )
625  {
626  _requiredFilesystemsPtr.reset( new std::set<std::string> );
627  std::set<std::string> & requiredFilesystems( *_requiredFilesystemsPtr );
629  std::inserter( requiredFilesystems, requiredFilesystems.end() ) );
630  }
631  return *_requiredFilesystemsPtr;
632  }
633 
635  } // namespace detail
638  } // namespace sat
641 } // namespace zypp