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