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