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