libzypp  11.13.5
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/Pool.h"
29 #include "zypp/Capability.h"
30 #include "zypp/Locale.h"
31 #include "zypp/PoolItem.h"
32 
35 
36 extern "C"
37 {
38 // Workaround libsolv project not providing a common include
39 // directory. (the -devel package does, but the git repo doesn't).
40 // #include <solv/repo_helix.h>
41 int repo_add_helix( ::Repo *repo, FILE *fp, int flags );
42 }
43 
44 using std::endl;
45 
46 #undef ZYPP_BASE_LOGGER_LOGGROUP
47 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::satpool"
48 
49 // ///////////////////////////////////////////////////////////////////
50 namespace zypp
51 {
52 
53  namespace sat
54  {
55 
57  namespace detail
58  {
59 
60  // MPL checks for satlib constants we redefine to avoid
61  // includes and defines.
62  BOOST_MPL_ASSERT_RELATION( noId, ==, STRID_NULL );
63  BOOST_MPL_ASSERT_RELATION( emptyId, ==, STRID_EMPTY );
64 
66  BOOST_MPL_ASSERT_RELATION( systemSolvableId, ==, SYSTEMSOLVABLE );
67 
68  BOOST_MPL_ASSERT_RELATION( solvablePrereqMarker, ==, SOLVABLE_PREREQMARKER );
69  BOOST_MPL_ASSERT_RELATION( solvableFileMarker, ==, SOLVABLE_FILEMARKER );
70 
76 
78 
79  const std::string & PoolImpl::systemRepoAlias()
80  {
81  static const std::string _val( "@System" );
82  return _val;
83  }
84 
85  const Pathname & sysconfigStoragePath()
86  {
87  static const Pathname _val( "/etc/sysconfig/storage" );
88  return _val;
89  }
90 
91 
93 
94  static void logSat( struct _Pool *, void *data, int type, const char *logString )
95  {
96  if ( type & (SOLV_FATAL|SOLV_ERROR) ) {
97  _ERR("libsolv") << logString;
98  } else if ( type & SOLV_DEBUG_STATS ) {
99  _DBG("libsolv") << logString;
100  } else {
101  _MIL("libsolv") << logString;
102  }
103  }
104 
105  detail::IdType PoolImpl::nsCallback( struct _Pool *, void * data, detail::IdType lhs, detail::IdType rhs )
106  {
107  // lhs: the namespace identifier, e.g. NAMESPACE:MODALIAS
108  // rhs: the value, e.g. pci:v0000104Cd0000840[01]sv*sd*bc*sc*i*
109  // return: 0 if not supportded
110  // 1 if supported by the system
111  // -1 AFAIK it's also possible to return a list of solvables that support it, but don't know how.
112 
113  static const detail::IdType RET_unsupported = 0;
114  static const detail::IdType RET_systemProperty = 1;
115  switch ( lhs )
116  {
117  case NAMESPACE_LANGUAGE:
118  {
119  static IdString en( "en" );
120  const std::tr1::unordered_set<IdString> & locale2Solver( reinterpret_cast<PoolImpl*>(data)->_locale2Solver );
121  if ( locale2Solver.empty() )
122  {
123  return rhs == en.id() ? RET_systemProperty : RET_unsupported;
124  }
125  return locale2Solver.find( IdString(rhs) ) != locale2Solver.end() ? RET_systemProperty : RET_unsupported;
126  }
127  break;
128 
129  case NAMESPACE_MODALIAS:
130  {
131  // modalias strings in capability may be hexencoded because rpm does not allow
132  // ',', ' ' or other special chars.
133  return target::Modalias::instance().query( str::hexdecode( IdString(rhs).c_str() ) )
134  ? RET_systemProperty
135  : RET_unsupported;
136  }
137  break;
138 
139  case NAMESPACE_FILESYSTEM:
140  {
141  const std::set<std::string> & requiredFilesystems( reinterpret_cast<PoolImpl*>(data)->requiredFilesystems() );
142  return requiredFilesystems.find( IdString(rhs).asString() ) != requiredFilesystems.end() ? RET_systemProperty : RET_unsupported;
143  }
144  break;
145 
146  case NAMESPACE_PRODUCTBUDDY:
147  {
148  PoolItem pi( (Solvable(rhs)) );
149  return( pi ? pi.buddy().id() : noId );
150  }
151 
152  break;
153  }
154 
155  WAR << "Unhandled " << Capability( lhs ) << " vs. " << Capability( rhs ) << endl;
156  return RET_unsupported;
157  }
158 
160  //
161  // METHOD NAME : PoolMember::myPool
162  // METHOD TYPE : PoolImpl
163  //
165  {
166  static PoolImpl _global;
167  return _global;
168  }
169 
171  //
172  // METHOD NAME : PoolImpl::PoolImpl
173  // METHOD TYPE : Ctor
174  //
176  : _pool( ::pool_create() )
177  {
178  MIL << "Creating sat-pool." << endl;
179  if ( ! _pool )
180  {
181  ZYPP_THROW( Exception( _("Can not create sat-pool.") ) );
182  }
183  // initialialize logging
184  if ( getenv("ZYPP_LIBSOLV_FULLLOG") || getenv("ZYPP_LIBSAT_FULLLOG") )
185  ::pool_setdebuglevel( _pool, 4 );
186  else if ( getenv("ZYPP_FULLLOG") )
187  ::pool_setdebuglevel( _pool, 2 );
188  else
189  ::pool_setdebugmask(_pool, SOLV_DEBUG_JOB|SOLV_DEBUG_STATS);
190 
191  ::pool_setdebugcallback( _pool, logSat, NULL );
192 
193  // set namespace callback
194  _pool->nscallback = &nsCallback;
195  _pool->nscallbackdata = (void*)this;
196  }
197 
199  //
200  // METHOD NAME : PoolImpl::~PoolImpl
201  // METHOD TYPE : Dtor
202  //
204  {
205  ::pool_free( _pool );
206  }
207 
209 
210  void PoolImpl::setDirty( const char * a1, const char * a2, const char * a3 )
211  {
212  if ( a1 )
213  {
214  if ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
215  else if ( a2 ) MIL << a1 << " " << a2 << endl;
216  else MIL << a1 << endl;
217  }
218  _serial.setDirty(); // pool content change
219  _availableLocalesPtr.reset(); // available locales may change
220  _multiversionListPtr.reset(); // re-evaluate ZConfig::multiversionSpec.
221 
222  // invaldate dependency/namespace related indices:
223  depSetDirty();
224  }
225 
226  void PoolImpl::depSetDirty( const char * a1, const char * a2, const char * a3 )
227  {
228  if ( a1 )
229  {
230  if ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
231  else if ( a2 ) MIL << a1 << " " << a2 << endl;
232  else MIL << a1 << endl;
233  }
234  ::pool_freewhatprovides( _pool );
235  }
236 
237  void PoolImpl::prepare() const
238  {
239  if ( _watcher.remember( _serial ) )
240  {
241  // After repo/solvable add/remove:
242  // set pool architecture
243  ::pool_setarch( _pool, ZConfig::instance().systemArchitecture().asString().c_str() );
244  }
245  if ( ! _pool->whatprovides )
246  {
247  MIL << "pool_createwhatprovides..." << endl;
248 
249  ::pool_addfileprovides( _pool );
250  ::pool_createwhatprovides( _pool );
251  }
252  if ( ! _pool->languages )
253  {
254  // initial seting
255  const_cast<PoolImpl*>(this)->setTextLocale( ZConfig::instance().textLocale() );
256  }
257  }
258 
260  {
261  // additional /etc/sysconfig/storage check:
262  static WatchFile sysconfigFile( sysconfigStoragePath(), WatchFile::NO_INIT );
263  if ( sysconfigFile.hasChanged() )
264  {
265  _requiredFilesystemsPtr.reset(); // recreated on demand
266  const_cast<PoolImpl*>(this)->depSetDirty( "/etc/sysconfig/storage change" );
267  }
268  // finally prepare as usual:
269  prepare();
270  }
271 
273 
274  ::_Repo * PoolImpl::_createRepo( const std::string & name_r )
275  {
276  setDirty(__FUNCTION__, name_r.c_str() );
277  ::_Repo * ret = ::repo_create( _pool, name_r.c_str() );
278  if ( ret && name_r == systemRepoAlias() )
279  ::pool_set_installed( _pool, ret );
280  return ret;
281  }
282 
283  void PoolImpl::_deleteRepo( ::_Repo * repo_r )
284  {
285  setDirty(__FUNCTION__, repo_r->name );
286  ::repo_free( repo_r, /*reuseids*/false );
287  eraseRepoInfo( repo_r );
288  if ( isSystemRepo( repo_r ) )
289  {
290  // systemRepo added
291  _onSystemByUserListPtr.reset(); // re-evaluate
292  }
293  }
294 
295  int PoolImpl::_addSolv( ::_Repo * repo_r, FILE * file_r )
296  {
297  setDirty(__FUNCTION__, repo_r->name );
298  int ret = ::repo_add_solv( repo_r, file_r, 0 );
299  if ( ret == 0 )
300  _postRepoAdd( repo_r );
301  return ret;
302  }
303 
304  int PoolImpl::_addHelix( ::_Repo * repo_r, FILE * file_r )
305  {
306  setDirty(__FUNCTION__, repo_r->name );
307  int ret = ::repo_add_helix( repo_r, file_r, 0 );
308  if ( ret == 0 )
309  _postRepoAdd( repo_r );
310  return 0;
311  }
312 
313  void PoolImpl::_postRepoAdd( ::_Repo * repo_r )
314  {
315  if ( ! isSystemRepo( repo_r ) )
316  {
317  // Filter out unwanted archs
318  std::set<detail::IdType> sysids;
319  {
320  Arch::CompatSet sysarchs( Arch::compatSet( ZConfig::instance().systemArchitecture() ) );
321  for_( it, sysarchs.begin(), sysarchs.end() )
322  sysids.insert( it->id() );
323 
324  // unfortunately libsolv treats src/nosrc as architecture:
325  sysids.insert( ARCH_SRC );
326  sysids.insert( ARCH_NOSRC );
327  }
328 
329  detail::IdType blockBegin = 0;
330  unsigned blockSize = 0;
331  for ( detail::IdType i = repo_r->start; i < repo_r->end; ++i )
332  {
333  ::_Solvable * s( _pool->solvables + i );
334  if ( s->repo == repo_r && sysids.find( s->arch ) == sysids.end() )
335  {
336  // Remember an unwanted arch entry:
337  if ( ! blockBegin )
338  blockBegin = i;
339  ++blockSize;
340  }
341  else if ( blockSize )
342  {
343  // Free remembered entries
344  ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
345  blockBegin = blockSize = 0;
346  }
347  }
348  if ( blockSize )
349  {
350  // Free remembered entries
351  ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
352  blockBegin = blockSize = 0;
353  }
354  }
355  else
356  {
357  // systemRepo added
358  _onSystemByUserListPtr.reset(); // re-evaluate
359  }
360  }
361 
362  detail::SolvableIdType PoolImpl::_addSolvables( ::_Repo * repo_r, unsigned count_r )
363  {
364  setDirty(__FUNCTION__, repo_r->name );
365  return ::repo_add_solvable_block( repo_r, count_r );
366  }
367 
368  void PoolImpl::setRepoInfo( RepoIdType id_r, const RepoInfo & info_r )
369  {
370  ::_Repo * repo( getRepo( id_r ) );
371  if ( repo )
372  {
373  bool dirty = false;
374 
375  // libsolv priority is based on '<', while yum's repoinfo
376  // uses 1(highest)->99(lowest). Thus we use -info_r.priority.
377  if ( repo->priority != int(-info_r.priority()) )
378  {
379  repo->priority = -info_r.priority();
380  dirty = true;
381  }
382 
383  // subpriority is used to e.g. prefer http over dvd iff
384  // both have same priority.
385  int mediaPriority( media::MediaPriority( info_r.url() ) );
386  if ( repo->subpriority != mediaPriority )
387  {
388  repo->subpriority = mediaPriority;
389  dirty = true;
390  }
391 
392  if ( dirty )
393  setDirty(__FUNCTION__, info_r.alias().c_str() );
394  }
395  _repoinfos[id_r] = info_r;
396  }
397 
399 
400  // need on demand and id based Locale
401  void _locale_hack( const LocaleSet & locales_r,
402  std::tr1::unordered_set<IdString> & locale2Solver )
403  {
404  std::tr1::unordered_set<IdString>( 2*locales_r.size() ).swap( locale2Solver );
405  for_( it, locales_r.begin(),locales_r.end() )
406  {
407  for ( Locale l( *it ); l != Locale::noCode; l = l.fallback() )
408  locale2Solver.insert( IdString( l.code() ) );
409  }
410  MIL << "New Solver Locales: " << locale2Solver << endl;
411  }
412 
413  void PoolImpl::setTextLocale( const Locale & locale_r )
414  {
415  std::vector<std::string> fallbacklist;
416  for ( Locale l( locale_r ); l != Locale::noCode; l = l.fallback() )
417  {
418  fallbacklist.push_back( l.code() );
419  }
420  dumpRangeLine( MIL << "pool_set_languages: ", fallbacklist.begin(), fallbacklist.end() ) << endl;
421 
422  std::vector<const char *> fallbacklist_cstr;
423  for_( it, fallbacklist.begin(), fallbacklist.end() )
424  {
425  fallbacklist_cstr.push_back( it->c_str() );
426  }
427  ::pool_set_languages( _pool, &fallbacklist_cstr.front(), fallbacklist_cstr.size() );
428  }
429 
430  void PoolImpl::setRequestedLocales( const LocaleSet & locales_r )
431  {
432  depSetDirty( "setRequestedLocales" );
433  _requestedLocales = locales_r;
434  MIL << "New RequestedLocales: " << locales_r << endl;
436  }
437 
438  bool PoolImpl::addRequestedLocale( const Locale & locale_r )
439  {
440  if ( _requestedLocales.insert( locale_r ).second )
441  {
442  depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
444  return true;
445  }
446  return false;
447  }
448 
449  bool PoolImpl::eraseRequestedLocale( const Locale & locale_r )
450  {
451  if ( _requestedLocales.erase( locale_r ) )
452  {
453  depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
455  return true;
456  }
457  return false;
458  }
459 
460  static void _getLocaleDeps( Capability cap_r, std::tr1::unordered_set<sat::detail::IdType> & store_r )
461  {
462  // Collect locales from any 'namespace:language(lang)' dependency
463  CapDetail detail( cap_r );
464  if ( detail.kind() == CapDetail::EXPRESSION )
465  {
466  switch ( detail.capRel() )
467  {
468  case CapDetail::CAP_AND:
469  case CapDetail::CAP_OR:
470  // expand
471  _getLocaleDeps( detail.lhs(), store_r );
472  _getLocaleDeps( detail.rhs(), store_r );
473  break;
474 
476  if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
477  {
478  store_r.insert( detail.rhs().id() );
479  }
480  break;
481 
482  case CapDetail::REL_NONE:
483  case CapDetail::CAP_WITH:
484  case CapDetail::CAP_ARCH:
485  break; // unwanted
486  }
487  }
488  }
489 
491  {
492  if ( !_availableLocalesPtr )
493  {
494  // Collect any 'namespace:language(ja)' dependencies
495  std::tr1::unordered_set<sat::detail::IdType> tmp;
496  Pool pool( Pool::instance() );
497  for_( it, pool.solvablesBegin(), pool.solvablesEnd() )
498  {
499  Capabilities cap( it->supplements() );
500  for_( cit, cap.begin(), cap.end() )
501  {
502  _getLocaleDeps( *cit, tmp );
503  }
504  }
505 #warning immediately build LocaleSet as soon as Loale is an Id based type
506  _availableLocalesPtr.reset( new LocaleSet(tmp.size()) );
507  for_( it, tmp.begin(), tmp.end() )
508  {
509  _availableLocalesPtr->insert( Locale( IdString(*it) ) );
510  }
511  }
512  return *_availableLocalesPtr;
513  }
514 
516  {
519 
520  const std::set<std::string> & multiversionSpec( ZConfig::instance().multiversionSpec() );
521  for_( it, multiversionSpec.begin(), multiversionSpec.end() )
522  {
523  static const std::string prefix( "provides:" );
524  if ( str::hasPrefix( *it, prefix ) )
525  {
526  WhatProvides provides( Capability( it->c_str() + prefix.size() ) );
527  if ( provides.empty() )
528  {
529  MIL << "Multiversion install not provided (" << *it << ")" << endl;
530  }
531  else
532  {
533  for_( pit, provides.begin(), provides.end() )
534  {
535  if ( multiversionList.insert( pit->ident() ).second )
536  MIL << "Multiversion install " << pit->ident() << " (" << *it << ")" << endl;
537  }
538  }
539  }
540  else
541  {
542  MIL << "Multiversion install " << *it << endl;
543  multiversionList.insert( IdString( *it ) );
544  }
545  }
546  }
547 
549  {
552 
553  Pathname root( ZConfig::instance().systemRoot() );
554  if ( root.empty() )
555  {
556  MIL << "Target not initialized." << endl;
557  return;
558  }
559  PathInfo pi( root / ZConfig::instance().historyLogFile() );
560  MIL << "onSystemByUserList from history: " << pi << endl;
561  if ( ! pi.isFile() )
562  return;
563 
564  // go and parse it: 'who' must constain an '@', then it was installed by user request.
565  // 2009-09-29 07:25:19|install|lirc-remotes|0.8.5-3.2|x86_64|root@opensuse|InstallationImage|a204211eb0...
566  std::ifstream infile( pi.path().c_str() );
567  for( iostr::EachLine in( infile ); in; in.next() )
568  {
569  const char * ch( (*in).c_str() );
570  // start with year
571  if ( *ch < '1' || '9' < *ch )
572  continue;
573  const char * sep1 = ::strchr( ch, '|' ); // | after date
574  if ( !sep1 )
575  continue;
576  ++sep1;
577  // if logs an install or delete
578  bool installs = true;
579  if ( ::strncmp( sep1, "install|", 8 ) )
580  {
581  if ( ::strncmp( sep1, "remove |", 8 ) )
582  continue; // no install and no remove
583  else
584  installs = false; // remove
585  }
586  sep1 += 8; // | after what
587  // get the package name
588  const char * sep2 = ::strchr( sep1, '|' ); // | after name
589  if ( !sep2 || sep1 == sep2 )
590  continue;
591  (*in)[sep2-ch] = '\0';
592  IdString pkg( sep1 );
593  // we're done, if a delete
594  if ( !installs )
595  {
596  onSystemByUserList.erase( pkg );
597  continue;
598  }
599  // now guess whether user installed or not (3rd next field contains 'user@host')
600  if ( (sep1 = ::strchr( sep2+1, '|' )) // | after version
601  && (sep1 = ::strchr( sep1+1, '|' )) // | after arch
602  && (sep2 = ::strchr( sep1+1, '|' )) ) // | after who
603  {
604  (*in)[sep2-ch] = '\0';
605  if ( ::strchr( sep1+1, '@' ) )
606  {
607  // by user
608  onSystemByUserList.insert( pkg );
609  continue;
610  }
611  }
612  }
613  MIL << "onSystemByUserList found: " << onSystemByUserList.size() << endl;
614  }
615 
616  const std::set<std::string> & PoolImpl::requiredFilesystems() const
617  {
618  if ( ! _requiredFilesystemsPtr )
619  {
620  _requiredFilesystemsPtr.reset( new std::set<std::string> );
621  std::set<std::string> & requiredFilesystems( *_requiredFilesystemsPtr );
623  std::inserter( requiredFilesystems, requiredFilesystems.end() ) );
624  }
625  return *_requiredFilesystemsPtr;
626  }
627 
629  } // namespace detail
632  } // namespace sat
635 } // namespace zypp