Imported Upstream version 14.45.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/Pool.h"
29 #include "zypp/Capability.h"
30 #include "zypp/Locale.h"
31 #include "zypp/PoolItem.h"
32
33 #include "zypp/target/modalias/Modalias.h"
34 #include "zypp/media/MediaPriority.h"
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 env
54   {
55     /**  */
56     inline int LIBSOLV_DEBUGMASK()
57     {
58       const char * envp = getenv("LIBSOLV_DEBUGMASK");
59       return envp ? str::strtonum<int>( envp ) : 0;
60     }
61   } // namespace env
62   ///////////////////////////////////////////////////////////////////
63   namespace sat
64   { /////////////////////////////////////////////////////////////////
65
66     ///////////////////////////////////////////////////////////////////
67     namespace detail
68     { /////////////////////////////////////////////////////////////////
69
70       // MPL checks for satlib constants we redefine to avoid
71       // includes and defines.
72       BOOST_MPL_ASSERT_RELATION( noId,                 ==, STRID_NULL );
73       BOOST_MPL_ASSERT_RELATION( emptyId,              ==, STRID_EMPTY );
74
75       BOOST_MPL_ASSERT_RELATION( noSolvableId,         ==, ID_NULL );
76       BOOST_MPL_ASSERT_RELATION( systemSolvableId,     ==, SYSTEMSOLVABLE );
77
78       BOOST_MPL_ASSERT_RELATION( solvablePrereqMarker, ==, SOLVABLE_PREREQMARKER );
79       BOOST_MPL_ASSERT_RELATION( solvableFileMarker,   ==, SOLVABLE_FILEMARKER );
80
81       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_AND,       ==, REL_AND );
82       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_OR,        ==, REL_OR );
83       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_WITH,      ==, REL_WITH );
84       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_NAMESPACE, ==, REL_NAMESPACE );
85       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_ARCH,      ==, REL_ARCH );
86
87      /////////////////////////////////////////////////////////////////
88
89       const std::string & PoolImpl::systemRepoAlias()
90       {
91         static const std::string _val( "@System" );
92         return _val;
93       }
94
95       const Pathname & sysconfigStoragePath()
96       {
97         static const Pathname _val( "/etc/sysconfig/storage" );
98         return _val;
99       }
100
101
102       /////////////////////////////////////////////////////////////////
103
104       static void logSat( struct _Pool *, void *data, int type, const char *logString )
105       {
106           if ( type & (SOLV_FATAL|SOLV_ERROR) ) {
107             _ERR("libsolv") << logString;
108           } else if ( type & SOLV_DEBUG_STATS ) {
109             _DBG("libsolv") << logString;
110           } else {
111             _MIL("libsolv") << logString;
112           }
113       }
114
115       detail::IdType PoolImpl::nsCallback( struct _Pool *, void * data, detail::IdType lhs, detail::IdType rhs )
116       {
117         // lhs:    the namespace identifier, e.g. NAMESPACE:MODALIAS
118         // rhs:    the value, e.g. pci:v0000104Cd0000840[01]sv*sd*bc*sc*i*
119         // return: 0 if not supportded
120         //         1 if supported by the system
121         //        -1  AFAIK it's also possible to return a list of solvables that support it, but don't know how.
122
123         static const detail::IdType RET_unsupported    = 0;
124         static const detail::IdType RET_systemProperty = 1;
125         switch ( lhs )
126         {
127           case NAMESPACE_LANGUAGE:
128           {
129             static IdString en( "en" );
130             const std::tr1::unordered_set<IdString> & locale2Solver( reinterpret_cast<PoolImpl*>(data)->_locale2Solver );
131             if ( locale2Solver.empty() )
132             {
133               return rhs == en.id() ? RET_systemProperty : RET_unsupported;
134             }
135             return locale2Solver.find( IdString(rhs) ) != locale2Solver.end() ? 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         // invaldate dependency/namespace related indices:
236         depSetDirty();
237       }
238
239       void PoolImpl::depSetDirty( 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         ::pool_freewhatprovides( _pool );
248       }
249
250       void PoolImpl::prepare() const
251       {
252         if ( _watcher.remember( _serial ) )
253         {
254           // After repo/solvable add/remove:
255           // set pool architecture
256           ::pool_setarch( _pool,  ZConfig::instance().systemArchitecture().asString().c_str() );
257         }
258         if ( ! _pool->whatprovides )
259         {
260           MIL << "pool_createwhatprovides..." << endl;
261
262           ::pool_addfileprovides( _pool );
263           ::pool_createwhatprovides( _pool );
264         }
265         if ( ! _pool->languages )
266         {
267           // initial seting
268           const_cast<PoolImpl*>(this)->setTextLocale( ZConfig::instance().textLocale() );
269         }
270       }
271
272       void PoolImpl::prepareForSolving() const
273       {
274         // additional /etc/sysconfig/storage check:
275         static WatchFile sysconfigFile( sysconfigStoragePath(), WatchFile::NO_INIT );
276         if ( sysconfigFile.hasChanged() )
277         {
278           _requiredFilesystemsPtr.reset(); // recreated on demand
279           const_cast<PoolImpl*>(this)->depSetDirty( "/etc/sysconfig/storage change" );
280         }
281         // finally prepare as usual:
282         prepare();
283       }
284
285       ///////////////////////////////////////////////////////////////////
286
287       ::_Repo * PoolImpl::_createRepo( const std::string & name_r )
288       {
289         setDirty(__FUNCTION__, name_r.c_str() );
290         ::_Repo * ret = ::repo_create( _pool, name_r.c_str() );
291         if ( ret && name_r == systemRepoAlias() )
292           ::pool_set_installed( _pool, ret );
293         return ret;
294       }
295
296       void PoolImpl::_deleteRepo( ::_Repo * repo_r )
297       {
298         setDirty(__FUNCTION__, repo_r->name );
299         if ( isSystemRepo( repo_r ) )
300           _autoinstalled.clear();
301         eraseRepoInfo( repo_r );
302         ::repo_free( repo_r, /*reuseids*/false );
303       }
304
305       int PoolImpl::_addSolv( ::_Repo * repo_r, FILE * file_r )
306       {
307         setDirty(__FUNCTION__, repo_r->name );
308         int ret = ::repo_add_solv( repo_r, file_r, 0 );
309         if ( ret == 0 )
310           _postRepoAdd( repo_r );
311         return ret;
312       }
313
314       int PoolImpl::_addHelix( ::_Repo * repo_r, FILE * file_r )
315       {
316         setDirty(__FUNCTION__, repo_r->name );
317         int ret = ::repo_add_helix( repo_r, file_r, 0 );
318         if ( ret == 0 )
319           _postRepoAdd( repo_r );
320         return 0;
321       }
322
323       void PoolImpl::_postRepoAdd( ::_Repo * repo_r )
324       {
325         if ( ! isSystemRepo( repo_r ) )
326         {
327             // Filter out unwanted archs
328           std::set<detail::IdType> sysids;
329           {
330             Arch::CompatSet sysarchs( Arch::compatSet( ZConfig::instance().systemArchitecture() ) );
331             for_( it, sysarchs.begin(), sysarchs.end() )
332               sysids.insert( it->id() );
333
334               // unfortunately libsolv treats src/nosrc as architecture:
335             sysids.insert( ARCH_SRC );
336             sysids.insert( ARCH_NOSRC );
337           }
338
339           detail::IdType blockBegin = 0;
340           unsigned       blockSize  = 0;
341           for ( detail::IdType i = repo_r->start; i < repo_r->end; ++i )
342           {
343               ::_Solvable * s( _pool->solvables + i );
344               if ( s->repo == repo_r && sysids.find( s->arch ) == sysids.end() )
345               {
346                 // Remember an unwanted arch entry:
347                 if ( ! blockBegin )
348                   blockBegin = i;
349                 ++blockSize;
350               }
351               else if ( blockSize )
352               {
353                 // Free remembered entries
354                   ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
355                   blockBegin = blockSize = 0;
356               }
357           }
358           if ( blockSize )
359           {
360               // Free remembered entries
361               ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
362               blockBegin = blockSize = 0;
363           }
364         }
365       }
366
367       detail::SolvableIdType PoolImpl::_addSolvables( ::_Repo * repo_r, unsigned count_r )
368       {
369         setDirty(__FUNCTION__, repo_r->name );
370         return ::repo_add_solvable_block( repo_r, count_r );
371       }
372
373       void PoolImpl::setRepoInfo( RepoIdType id_r, const RepoInfo & info_r )
374       {
375         ::_Repo * repo( getRepo( id_r ) );
376         if ( repo )
377         {
378           bool dirty = false;
379
380           // libsolv priority is based on '<', while yum's repoinfo
381           // uses 1(highest)->99(lowest). Thus we use -info_r.priority.
382           if ( repo->priority != int(-info_r.priority()) )
383           {
384             repo->priority = -info_r.priority();
385             dirty = true;
386           }
387
388           // subpriority is used to e.g. prefer http over dvd iff
389           // both have same priority.
390           int mediaPriority( media::MediaPriority( info_r.url() ) );
391           if ( repo->subpriority != mediaPriority )
392           {
393             repo->subpriority = mediaPriority;
394             dirty = true;
395           }
396
397           if ( dirty )
398             setDirty(__FUNCTION__, info_r.alias().c_str() );
399         }
400         _repoinfos[id_r] = info_r;
401       }
402
403       ///////////////////////////////////////////////////////////////////
404
405       // need on demand and id based Locale
406       void _locale_hack( const LocaleSet & locales_r,
407                          std::tr1::unordered_set<IdString> & locale2Solver )
408       {
409         std::tr1::unordered_set<IdString>( 2*locales_r.size() ).swap( locale2Solver );
410         for_( it, locales_r.begin(),locales_r.end() )
411         {
412           for ( Locale l( *it ); l != Locale::noCode; l = l.fallback() )
413             locale2Solver.insert( IdString( l.code() ) );
414         }
415         MIL << "New Solver Locales: " << locale2Solver << endl;
416       }
417
418       void PoolImpl::setTextLocale( const Locale & locale_r )
419       {
420         std::vector<std::string> fallbacklist;
421         for ( Locale l( locale_r ); l != Locale::noCode; l = l.fallback() )
422         {
423           fallbacklist.push_back( l.code() );
424         }
425         dumpRangeLine( MIL << "pool_set_languages: ", fallbacklist.begin(), fallbacklist.end() ) << endl;
426
427         std::vector<const char *> fallbacklist_cstr;
428         for_( it, fallbacklist.begin(), fallbacklist.end() )
429         {
430           fallbacklist_cstr.push_back( it->c_str() );
431         }
432         ::pool_set_languages( _pool, &fallbacklist_cstr.front(), fallbacklist_cstr.size() );
433       }
434
435       void PoolImpl::setRequestedLocales( const LocaleSet & locales_r )
436       {
437         depSetDirty( "setRequestedLocales" );
438         _requestedLocales = locales_r;
439         MIL << "New RequestedLocales: " << locales_r << endl;
440         _locale_hack( _requestedLocales, _locale2Solver );
441       }
442
443       bool PoolImpl::addRequestedLocale( const Locale & locale_r )
444       {
445         if ( _requestedLocales.insert( locale_r ).second )
446         {
447           depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
448           _locale_hack( _requestedLocales, _locale2Solver );
449           return true;
450         }
451         return false;
452       }
453
454       bool PoolImpl::eraseRequestedLocale( const Locale & locale_r )
455       {
456         if ( _requestedLocales.erase( locale_r ) )
457         {
458           depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
459           _locale_hack( _requestedLocales, _locale2Solver );
460           return true;
461         }
462         return false;
463       }
464
465       static void _getLocaleDeps( Capability cap_r, std::tr1::unordered_set<sat::detail::IdType> & store_r )
466       {
467         // Collect locales from any 'namespace:language(lang)' dependency
468         CapDetail detail( cap_r );
469         if ( detail.kind() == CapDetail::EXPRESSION )
470         {
471           switch ( detail.capRel() )
472           {
473             case CapDetail::CAP_AND:
474             case CapDetail::CAP_OR:
475               // expand
476               _getLocaleDeps( detail.lhs(), store_r );
477               _getLocaleDeps( detail.rhs(), store_r );
478               break;
479
480             case CapDetail::CAP_NAMESPACE:
481               if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
482               {
483                 store_r.insert( detail.rhs().id() );
484               }
485               break;
486
487             case CapDetail::REL_NONE:
488             case CapDetail::CAP_WITH:
489             case CapDetail::CAP_ARCH:
490               break; // unwanted
491           }
492         }
493       }
494
495       const LocaleSet & PoolImpl::getAvailableLocales() const
496       {
497         if ( !_availableLocalesPtr )
498         {
499           // Collect any 'namespace:language(ja)' dependencies
500           std::tr1::unordered_set<sat::detail::IdType> tmp;
501           Pool pool( Pool::instance() );
502           for_( it, pool.solvablesBegin(), pool.solvablesEnd() )
503           {
504             Capabilities cap( it->supplements() );
505             for_( cit, cap.begin(), cap.end() )
506             {
507               _getLocaleDeps( *cit, tmp );
508             }
509           }
510 #warning immediately build LocaleSet as soon as Loale is an Id based type
511           _availableLocalesPtr.reset( new LocaleSet(tmp.size()) );
512           for_( it, tmp.begin(), tmp.end() )
513           {
514             _availableLocalesPtr->insert( Locale( IdString(*it) ) );
515           }
516         }
517         return *_availableLocalesPtr;
518       }
519
520       void PoolImpl::multiversionListInit() const
521       {
522         _multiversionListPtr.reset( new MultiversionList );
523         MultiversionList & multiversionList( *_multiversionListPtr );
524
525         const std::set<std::string> & multiversionSpec( ZConfig::instance().multiversionSpec() );
526         for_( it, multiversionSpec.begin(), multiversionSpec.end() )
527         {
528           static const std::string prefix( "provides:" );
529           if ( str::hasPrefix( *it, prefix ) )
530           {
531             WhatProvides provides( Capability( it->c_str() + prefix.size() ) );
532             if ( provides.empty() )
533             {
534               MIL << "Multiversion install not provided (" << *it << ")" << endl;
535             }
536             else
537             {
538               for_( pit, provides.begin(), provides.end() )
539               {
540                 if ( multiversionList.insert( pit->ident() ).second )
541                   MIL << "Multiversion install " << pit->ident() << " (" << *it << ")" << endl;
542               }
543             }
544           }
545           else
546           {
547             MIL << "Multiversion install " << *it << endl;
548             multiversionList.insert( IdString( *it ) );
549           }
550         }
551       }
552
553       const std::set<std::string> & PoolImpl::requiredFilesystems() const
554       {
555         if ( ! _requiredFilesystemsPtr )
556         {
557           _requiredFilesystemsPtr.reset( new std::set<std::string> );
558           std::set<std::string> & requiredFilesystems( *_requiredFilesystemsPtr );
559           str::split( base::sysconfig::read( sysconfigStoragePath() )["USED_FS_LIST"],
560                       std::inserter( requiredFilesystems, requiredFilesystems.end() ) );
561         }
562         return *_requiredFilesystemsPtr;
563       }
564
565       /////////////////////////////////////////////////////////////////
566     } // namespace detail
567     ///////////////////////////////////////////////////////////////////
568     /////////////////////////////////////////////////////////////////
569   } // namespace sat
570   ///////////////////////////////////////////////////////////////////
571   /////////////////////////////////////////////////////////////////
572 } // namespace zypp
573 ///////////////////////////////////////////////////////////////////