Imported Upstream version 16.0.3
[platform/upstream/libzypp.git] / zypp / sat / detail / PoolImpl.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/sat/detail/PoolImpl.cc
10  *
11 */
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
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"
33
34 #include "zypp/target/modalias/Modalias.h"
35 #include "zypp/media/MediaPriority.h"
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 {
53   /////////////////////////////////////////////////////////////////
54   namespace env
55   {
56     /**  */
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
63   ///////////////////////////////////////////////////////////////////
64   namespace sat
65   { /////////////////////////////////////////////////////////////////
66
67     ///////////////////////////////////////////////////////////////////
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
76       BOOST_MPL_ASSERT_RELATION( noSolvableId,         ==, ID_NULL );
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
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 );
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
92       /////////////////////////////////////////////////////////////////
93
94       const bool resusePoolIDs = true;
95
96       const std::string & PoolImpl::systemRepoAlias()
97       {
98         static const std::string _val( "@System" );
99         return _val;
100       }
101
102       const Pathname & sysconfigStoragePath()
103       {
104         static const Pathname _val( "/etc/sysconfig/storage" );
105         return _val;
106       }
107
108       /////////////////////////////////////////////////////////////////
109
110       static void logSat( CPool *, void *data, int type, const char *logString )
111       {
112           if ( type & (SOLV_FATAL|SOLV_ERROR) ) {
113             L_ERR("libsolv") << logString;
114           } else if ( type & SOLV_DEBUG_STATS ) {
115             L_DBG("libsolv") << logString;
116           } else {
117             L_MIL("libsolv") << logString;
118           }
119       }
120
121       detail::IdType PoolImpl::nsCallback( CPool *, void * data, detail::IdType lhs, detail::IdType rhs )
122       {
123         // lhs:    the namespace identifier, e.g. NAMESPACE:MODALIAS
124         // rhs:    the value, e.g. pci:v0000104Cd0000840[01]sv*sd*bc*sc*i*
125         // return: 0 if not supportded
126         //         1 if supported by the system
127         //        -1  AFAIK it's also possible to return a list of solvables that support it, but don't know how.
128
129         static const detail::IdType RET_unsupported    = 0;
130         static const detail::IdType RET_systemProperty = 1;
131         switch ( lhs )
132         {
133           case NAMESPACE_LANGUAGE:
134           {
135             const TrackedLocaleIds & localeIds( reinterpret_cast<PoolImpl*>(data)->trackedLocaleIds() );
136             return localeIds.contains( IdString(rhs) ) ? RET_systemProperty : RET_unsupported;
137           }
138           break;
139
140           case NAMESPACE_MODALIAS:
141           {
142             // modalias strings in capability may be hexencoded because rpm does not allow
143             // ',', ' ' or other special chars.
144             return target::Modalias::instance().query( str::hexdecode( IdString(rhs).c_str() ) )
145                 ? RET_systemProperty
146               : RET_unsupported;
147           }
148           break;
149
150           case NAMESPACE_FILESYSTEM:
151           {
152             const std::set<std::string> & requiredFilesystems( reinterpret_cast<PoolImpl*>(data)->requiredFilesystems() );
153             return requiredFilesystems.find( IdString(rhs).asString() ) != requiredFilesystems.end() ? RET_systemProperty : RET_unsupported;
154           }
155           break;
156
157         }
158
159         WAR << "Unhandled " << Capability( lhs ) << " vs. " << Capability( rhs ) << endl;
160         return RET_unsupported;
161       }
162
163       ///////////////////////////////////////////////////////////////////
164       //
165       //        METHOD NAME : PoolMember::myPool
166       //        METHOD TYPE : PoolImpl
167       //
168       PoolImpl & PoolMember::myPool()
169       {
170         static PoolImpl _global;
171         return _global;
172       }
173
174       ///////////////////////////////////////////////////////////////////
175       //
176       //        METHOD NAME : PoolImpl::PoolImpl
177       //        METHOD TYPE : Ctor
178       //
179       PoolImpl::PoolImpl()
180       : _pool( ::pool_create() )
181       {
182         MIL << "Creating sat-pool." << endl;
183         if ( ! _pool )
184         {
185           ZYPP_THROW( Exception( _("Can not create sat-pool.") ) );
186         }
187         // by now we support only a RPM backend
188         ::pool_setdisttype(_pool, DISTTYPE_RPM );
189
190         // initialialize logging
191         if ( env::LIBSOLV_DEBUGMASK() )
192         {
193           ::pool_setdebugmask(_pool, env::LIBSOLV_DEBUGMASK() );
194         }
195         else
196         {
197           if ( getenv("ZYPP_LIBSOLV_FULLLOG") || getenv("ZYPP_LIBSAT_FULLLOG") )
198             ::pool_setdebuglevel( _pool, 3 );
199           else if ( getenv("ZYPP_FULLLOG") )
200             ::pool_setdebuglevel( _pool, 2 );
201           else
202             ::pool_setdebugmask(_pool, SOLV_DEBUG_JOB|SOLV_DEBUG_STATS );
203         }
204
205         ::pool_setdebugcallback( _pool, logSat, NULL );
206
207         // set namespace callback
208         _pool->nscallback = &nsCallback;
209         _pool->nscallbackdata = (void*)this;
210       }
211
212       ///////////////////////////////////////////////////////////////////
213       //
214       //        METHOD NAME : PoolImpl::~PoolImpl
215       //        METHOD TYPE : Dtor
216       //
217       PoolImpl::~PoolImpl()
218       {
219         ::pool_free( _pool );
220       }
221
222      ///////////////////////////////////////////////////////////////////
223
224       void PoolImpl::setDirty( const char * a1, const char * a2, const char * a3 )
225       {
226         if ( a1 )
227         {
228           if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
229           else if ( a2 ) MIL << a1 << " " << a2 << endl;
230           else           MIL << a1 << endl;
231         }
232         _serial.setDirty();           // pool content change
233         _availableLocalesPtr.reset(); // available locales may change
234         _multiversionListPtr.reset(); // re-evaluate ZConfig::multiversionSpec.
235
236         depSetDirty();  // invaldate dependency/namespace related indices
237       }
238
239       void PoolImpl::localeSetDirty( const char * a1, const char * a2, const char * a3 )
240       {
241         if ( a1 )
242         {
243           if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
244           else if ( a2 ) MIL << a1 << " " << a2 << endl;
245           else           MIL << a1 << endl;
246         }
247         _trackedLocaleIdsPtr.reset();   // requested locales changed
248         depSetDirty();  // invaldate dependency/namespace related indices
249       }
250
251       void PoolImpl::depSetDirty( const char * a1, const char * a2, const char * a3 )
252       {
253         if ( a1 )
254         {
255           if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
256           else if ( a2 ) MIL << a1 << " " << a2 << endl;
257           else           MIL << a1 << endl;
258         }
259         ::pool_freewhatprovides( _pool );
260       }
261
262       void PoolImpl::prepare() const
263       {
264         // additional /etc/sysconfig/storage check:
265         static WatchFile sysconfigFile( sysconfigStoragePath(), WatchFile::NO_INIT );
266         if ( sysconfigFile.hasChanged() )
267         {
268           _requiredFilesystemsPtr.reset(); // recreated on demand
269           const_cast<PoolImpl*>(this)->depSetDirty( "/etc/sysconfig/storage change" );
270         }
271         if ( _watcher.remember( _serial ) )
272         {
273           // After repo/solvable add/remove:
274           // set pool architecture
275           ::pool_setarch( _pool,  ZConfig::instance().systemArchitecture().asString().c_str() );
276         }
277         if ( ! _pool->whatprovides )
278         {
279           MIL << "pool_createwhatprovides..." << endl;
280
281           ::pool_addfileprovides( _pool );
282           ::pool_createwhatprovides( _pool );
283         }
284         if ( ! _pool->languages )
285         {
286           // initial seting
287           const_cast<PoolImpl*>(this)->setTextLocale( ZConfig::instance().textLocale() );
288         }
289       }
290
291       ///////////////////////////////////////////////////////////////////
292
293       CRepo * PoolImpl::_createRepo( const std::string & name_r )
294       {
295         setDirty(__FUNCTION__, name_r.c_str() );
296         CRepo * ret = ::repo_create( _pool, name_r.c_str() );
297         if ( ret && name_r == systemRepoAlias() )
298           ::pool_set_installed( _pool, ret );
299         return ret;
300       }
301
302       void PoolImpl::_deleteRepo( CRepo * repo_r )
303       {
304         setDirty(__FUNCTION__, repo_r->name );
305         if ( isSystemRepo( repo_r ) )
306           _autoinstalled.clear();
307         eraseRepoInfo( repo_r );
308         ::repo_free( repo_r, resusePoolIDs );
309         if ( resusePoolIDs && !_pool->urepos )  // If the last repo is removed clear the pool to actually reuse all IDs.
310           ::pool_freeallrepos( _pool, resusePoolIDs );
311       }
312
313       int PoolImpl::_addSolv( CRepo * repo_r, FILE * file_r )
314       {
315         setDirty(__FUNCTION__, repo_r->name );
316         int ret = ::repo_add_solv( repo_r, file_r, 0 );
317         if ( ret == 0 )
318           _postRepoAdd( repo_r );
319         return ret;
320       }
321
322       int PoolImpl::_addHelix( CRepo * repo_r, FILE * file_r )
323       {
324         setDirty(__FUNCTION__, repo_r->name );
325         int ret = ::repo_add_helix( repo_r, file_r, 0 );
326         if ( ret == 0 )
327           _postRepoAdd( repo_r );
328         return 0;
329       }
330
331       void PoolImpl::_postRepoAdd( CRepo * repo_r )
332       {
333         if ( ! isSystemRepo( repo_r ) )
334         {
335             // Filter out unwanted archs
336           std::set<detail::IdType> sysids;
337           {
338             Arch::CompatSet sysarchs( Arch::compatSet( ZConfig::instance().systemArchitecture() ) );
339             for_( it, sysarchs.begin(), sysarchs.end() )
340               sysids.insert( it->id() );
341
342               // unfortunately libsolv treats src/nosrc as architecture:
343             sysids.insert( ARCH_SRC );
344             sysids.insert( ARCH_NOSRC );
345           }
346
347           detail::IdType blockBegin = 0;
348           unsigned       blockSize  = 0;
349           for ( detail::IdType i = repo_r->start; i < repo_r->end; ++i )
350           {
351               CSolvable * s( _pool->solvables + i );
352               if ( s->repo == repo_r && sysids.find( s->arch ) == sysids.end() )
353               {
354                 // Remember an unwanted arch entry:
355                 if ( ! blockBegin )
356                   blockBegin = i;
357                 ++blockSize;
358               }
359               else if ( blockSize )
360               {
361                 // Free remembered entries
362                   ::repo_free_solvable_block( repo_r, blockBegin, blockSize, resusePoolIDs );
363                   blockBegin = blockSize = 0;
364               }
365           }
366           if ( blockSize )
367           {
368               // Free remembered entries
369               ::repo_free_solvable_block( repo_r, blockBegin, blockSize, resusePoolIDs );
370               blockBegin = blockSize = 0;
371           }
372         }
373       }
374
375       detail::SolvableIdType PoolImpl::_addSolvables( CRepo * repo_r, unsigned count_r )
376       {
377         setDirty(__FUNCTION__, repo_r->name );
378         return ::repo_add_solvable_block( repo_r, count_r );
379       }
380
381       void PoolImpl::setRepoInfo( RepoIdType id_r, const RepoInfo & info_r )
382       {
383         CRepo * repo( getRepo( id_r ) );
384         if ( repo )
385         {
386           bool dirty = false;
387
388           // libsolv priority is based on '<', while yum's repoinfo
389           // uses 1(highest)->99(lowest). Thus we use -info_r.priority.
390           if ( repo->priority != int(-info_r.priority()) )
391           {
392             repo->priority = -info_r.priority();
393             dirty = true;
394           }
395
396           // subpriority is used to e.g. prefer http over dvd iff
397           // both have same priority.
398           int mediaPriority( media::MediaPriority( info_r.url() ) );
399           if ( repo->subpriority != mediaPriority )
400           {
401             repo->subpriority = mediaPriority;
402             dirty = true;
403           }
404
405           if ( dirty )
406             setDirty(__FUNCTION__, info_r.alias().c_str() );
407         }
408         _repoinfos[id_r] = info_r;
409       }
410
411       ///////////////////////////////////////////////////////////////////
412
413       void PoolImpl::setTextLocale( const Locale & locale_r )
414       {
415         std::vector<std::string> fallbacklist;
416         for ( Locale l( locale_r ); l; 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::initRequestedLocales( const LocaleSet & locales_r )
431       {
432         if ( _requestedLocalesTracker.setInitial( locales_r ) )
433         {
434           localeSetDirty( "initRequestedLocales" );
435           MIL << "Init RequestedLocales: " << _requestedLocalesTracker << " =" << locales_r << endl;
436         }
437       }
438
439       void PoolImpl::setRequestedLocales( const LocaleSet & locales_r )
440       {
441         if ( _requestedLocalesTracker.set( locales_r ) )
442         {
443           localeSetDirty( "setRequestedLocales" );
444           MIL << "New RequestedLocales: " << _requestedLocalesTracker << " =" << locales_r << endl;
445         }
446       }
447
448       bool PoolImpl::addRequestedLocale( const Locale & locale_r )
449       {
450         bool done = _requestedLocalesTracker.add( locale_r );
451         if ( done )
452         {
453           localeSetDirty( "addRequestedLocale", locale_r.code().c_str() );
454           MIL << "New RequestedLocales: " << _requestedLocalesTracker << " +" << locale_r << endl;
455         }
456         return done;
457       }
458
459       bool PoolImpl::eraseRequestedLocale( const Locale & locale_r )
460       {
461         bool done = _requestedLocalesTracker.remove( locale_r );
462         if ( done )
463         {
464           localeSetDirty( "addRequestedLocale", locale_r.code().c_str() );
465           MIL << "New RequestedLocales: " << _requestedLocalesTracker << " -" << locale_r << endl;
466         }
467         return done;
468       }
469
470
471       const PoolImpl::TrackedLocaleIds & PoolImpl::trackedLocaleIds() const
472       {
473         if ( ! _trackedLocaleIdsPtr )
474         {
475           _trackedLocaleIdsPtr.reset( new TrackedLocaleIds );
476
477           const base::SetTracker<LocaleSet> &   localesTracker( _requestedLocalesTracker );
478           TrackedLocaleIds &                    localeIds( *_trackedLocaleIdsPtr );
479
480           // Add current locales+fallback except for added ones
481           for ( Locale lang: localesTracker.current() )
482           {
483             if ( localesTracker.wasAdded( lang ) )
484               continue;
485             for ( ; lang; lang = lang.fallback() )
486             { localeIds.current().insert( IdString(lang) ); }
487           }
488
489           // Add added locales+fallback except they are already in current
490           for ( Locale lang: localesTracker.added() )
491           {
492             for ( ; lang && localeIds.current().insert( IdString(lang) ).second; lang = lang.fallback() )
493             { localeIds.added().insert( IdString(lang) ); }
494           }
495
496           // Add removed locales+fallback except they are still in current
497           for ( Locale lang: localesTracker.removed() )
498           {
499             for ( ; lang && ! localeIds.current().count( IdString(lang) ); lang = lang.fallback() )
500             { localeIds.removed().insert( IdString(lang) ); }
501           }
502
503           // Assert that TrackedLocaleIds::current is not empty.
504           // If, so fill in LanguageCode::enCode as last resort.
505           if ( localeIds.current().empty() )
506           { localeIds.current().insert( IdString(Locale::enCode) ); }
507         }
508         return *_trackedLocaleIdsPtr;
509       }
510
511
512       static void _getLocaleDeps( const Capability & cap_r, LocaleSet & store_r )
513       {
514         // Collect locales from any 'namespace:language(lang)' dependency
515         CapDetail detail( cap_r );
516         if ( detail.kind() == CapDetail::EXPRESSION )
517         {
518           switch ( detail.capRel() )
519           {
520             case CapDetail::CAP_AND:
521             case CapDetail::CAP_OR:
522               // expand
523               _getLocaleDeps( detail.lhs(), store_r );
524               _getLocaleDeps( detail.rhs(), store_r );
525               break;
526
527             case CapDetail::CAP_NAMESPACE:
528               if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
529               {
530                 store_r.insert( Locale( IdString(detail.rhs().id()) ) );
531               }
532               break;
533
534             case CapDetail::REL_NONE:
535             case CapDetail::CAP_WITH:
536             case CapDetail::CAP_ARCH:
537               break; // unwanted
538           }
539         }
540       }
541
542       const LocaleSet & PoolImpl::getAvailableLocales() const
543       {
544         if ( !_availableLocalesPtr )
545         {
546           _availableLocalesPtr.reset( new LocaleSet );
547           LocaleSet & localeSet( *_availableLocalesPtr );
548
549           for ( const Solvable & pi : Pool::instance().solvables() )
550           {
551             for ( const Capability & cap : pi.supplements() )
552             {
553               _getLocaleDeps( cap, localeSet );
554             }
555           }
556         }
557         return *_availableLocalesPtr;
558       }
559
560       ///////////////////////////////////////////////////////////////////
561
562       void PoolImpl::multiversionListInit() const
563       {
564         _multiversionListPtr.reset( new MultiversionList );
565         MultiversionList & multiversionList( *_multiversionListPtr );
566         
567         MultiversionList::size_type size = 0;
568         for ( const std::string & spec : ZConfig::instance().multiversionSpec() )
569         {
570           static const std::string prefix( "provides:" );
571           bool provides = str::hasPrefix( spec, prefix );
572
573           for ( Solvable solv : WhatProvides( Capability( provides ? spec.c_str() + prefix.size() : spec.c_str() ) ) )
574           {
575             if ( provides || solv.ident() == spec )
576               multiversionList.insert( solv );
577           }
578
579           MultiversionList::size_type nsize = multiversionList.size();
580           MIL << "Multiversion install " << spec << ": " << (nsize-size) << " matches" << endl;
581           size = nsize;
582         }
583       }
584
585       void PoolImpl::multiversionSpecChanged()
586       { _multiversionListPtr.reset(); }
587
588       const PoolImpl::MultiversionList & PoolImpl::multiversionList() const
589       {
590         if ( ! _multiversionListPtr )
591           multiversionListInit();
592         return *_multiversionListPtr;
593       }
594
595       bool PoolImpl::isMultiversion( const Solvable & solv_r ) const
596       { return multiversionList().contains( solv_r ); }
597
598       ///////////////////////////////////////////////////////////////////
599
600       const std::set<std::string> & PoolImpl::requiredFilesystems() const
601       {
602         if ( ! _requiredFilesystemsPtr )
603         {
604           _requiredFilesystemsPtr.reset( new std::set<std::string> );
605           std::set<std::string> & requiredFilesystems( *_requiredFilesystemsPtr );
606           str::split( base::sysconfig::read( sysconfigStoragePath() )["USED_FS_LIST"],
607                       std::inserter( requiredFilesystems, requiredFilesystems.end() ) );
608         }
609         return *_requiredFilesystemsPtr;
610       }
611
612       /////////////////////////////////////////////////////////////////
613     } // namespace detail
614     ///////////////////////////////////////////////////////////////////
615     /////////////////////////////////////////////////////////////////
616   } // namespace sat
617   ///////////////////////////////////////////////////////////////////
618   /////////////////////////////////////////////////////////////////
619 } // namespace zypp
620 ///////////////////////////////////////////////////////////////////