NAMESPACE_PRODUCTBUDDY is gone from libsolv
[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         // initialialize logging
187         if ( env::LIBSOLV_DEBUGMASK() )
188         {
189           ::pool_setdebugmask(_pool, env::LIBSOLV_DEBUGMASK() );
190         }
191         else
192         {
193           if ( getenv("ZYPP_LIBSOLV_FULLLOG") || getenv("ZYPP_LIBSAT_FULLLOG") )
194             ::pool_setdebuglevel( _pool, 3 );
195           else if ( getenv("ZYPP_FULLLOG") )
196             ::pool_setdebuglevel( _pool, 2 );
197           else
198             ::pool_setdebugmask(_pool, SOLV_DEBUG_JOB|SOLV_DEBUG_STATS );
199         }
200
201         ::pool_setdebugcallback( _pool, logSat, NULL );
202
203         // set namespace callback
204         _pool->nscallback = &nsCallback;
205         _pool->nscallbackdata = (void*)this;
206       }
207
208       ///////////////////////////////////////////////////////////////////
209       //
210       //        METHOD NAME : PoolImpl::~PoolImpl
211       //        METHOD TYPE : Dtor
212       //
213       PoolImpl::~PoolImpl()
214       {
215         ::pool_free( _pool );
216       }
217
218      ///////////////////////////////////////////////////////////////////
219
220       void PoolImpl::setDirty( const char * a1, const char * a2, const char * a3 )
221       {
222         if ( a1 )
223         {
224           if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
225           else if ( a2 ) MIL << a1 << " " << a2 << endl;
226           else           MIL << a1 << endl;
227         }
228         _serial.setDirty();           // pool content change
229         _availableLocalesPtr.reset(); // available locales may change
230         _multiversionListPtr.reset(); // re-evaluate ZConfig::multiversionSpec.
231
232         // invaldate dependency/namespace related indices:
233         depSetDirty();
234       }
235
236       void PoolImpl::depSetDirty( const char * a1, const char * a2, const char * a3 )
237       {
238         if ( a1 )
239         {
240           if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
241           else if ( a2 ) MIL << a1 << " " << a2 << endl;
242           else           MIL << a1 << endl;
243         }
244         ::pool_freewhatprovides( _pool );
245       }
246
247       void PoolImpl::prepare() const
248       {
249         if ( _watcher.remember( _serial ) )
250         {
251           // After repo/solvable add/remove:
252           // set pool architecture
253           ::pool_setarch( _pool,  ZConfig::instance().systemArchitecture().asString().c_str() );
254         }
255         if ( ! _pool->whatprovides )
256         {
257           MIL << "pool_createwhatprovides..." << endl;
258
259           ::pool_addfileprovides( _pool );
260           ::pool_createwhatprovides( _pool );
261         }
262         if ( ! _pool->languages )
263         {
264           // initial seting
265           const_cast<PoolImpl*>(this)->setTextLocale( ZConfig::instance().textLocale() );
266         }
267       }
268
269       void PoolImpl::prepareForSolving() const
270       {
271         // additional /etc/sysconfig/storage check:
272         static WatchFile sysconfigFile( sysconfigStoragePath(), WatchFile::NO_INIT );
273         if ( sysconfigFile.hasChanged() )
274         {
275           _requiredFilesystemsPtr.reset(); // recreated on demand
276           const_cast<PoolImpl*>(this)->depSetDirty( "/etc/sysconfig/storage change" );
277         }
278         // finally prepare as usual:
279         prepare();
280       }
281
282       ///////////////////////////////////////////////////////////////////
283
284       ::_Repo * PoolImpl::_createRepo( const std::string & name_r )
285       {
286         setDirty(__FUNCTION__, name_r.c_str() );
287         ::_Repo * ret = ::repo_create( _pool, name_r.c_str() );
288         if ( ret && name_r == systemRepoAlias() )
289           ::pool_set_installed( _pool, ret );
290         return ret;
291       }
292
293       void PoolImpl::_deleteRepo( ::_Repo * repo_r )
294       {
295         setDirty(__FUNCTION__, repo_r->name );
296         ::repo_free( repo_r, /*reuseids*/false );
297         eraseRepoInfo( repo_r );
298         if ( isSystemRepo( repo_r ) )
299         {
300           // systemRepo added
301           _onSystemByUserListPtr.reset(); // re-evaluate
302         }
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         else
366         {
367           // systemRepo added
368           _onSystemByUserListPtr.reset(); // re-evaluate
369         }
370       }
371
372       detail::SolvableIdType PoolImpl::_addSolvables( ::_Repo * repo_r, unsigned count_r )
373       {
374         setDirty(__FUNCTION__, repo_r->name );
375         return ::repo_add_solvable_block( repo_r, count_r );
376       }
377
378       void PoolImpl::setRepoInfo( RepoIdType id_r, const RepoInfo & info_r )
379       {
380         ::_Repo * repo( getRepo( id_r ) );
381         if ( repo )
382         {
383           bool dirty = false;
384
385           // libsolv priority is based on '<', while yum's repoinfo
386           // uses 1(highest)->99(lowest). Thus we use -info_r.priority.
387           if ( repo->priority != int(-info_r.priority()) )
388           {
389             repo->priority = -info_r.priority();
390             dirty = true;
391           }
392
393           // subpriority is used to e.g. prefer http over dvd iff
394           // both have same priority.
395           int mediaPriority( media::MediaPriority( info_r.url() ) );
396           if ( repo->subpriority != mediaPriority )
397           {
398             repo->subpriority = mediaPriority;
399             dirty = true;
400           }
401
402           if ( dirty )
403             setDirty(__FUNCTION__, info_r.alias().c_str() );
404         }
405         _repoinfos[id_r] = info_r;
406       }
407
408       ///////////////////////////////////////////////////////////////////
409
410       // need on demand and id based Locale
411       void _locale_hack( const LocaleSet & locales_r,
412                          std::tr1::unordered_set<IdString> & locale2Solver )
413       {
414         std::tr1::unordered_set<IdString>( 2*locales_r.size() ).swap( locale2Solver );
415         for_( it, locales_r.begin(),locales_r.end() )
416         {
417           for ( Locale l( *it ); l != Locale::noCode; l = l.fallback() )
418             locale2Solver.insert( IdString( l.code() ) );
419         }
420         MIL << "New Solver Locales: " << locale2Solver << endl;
421       }
422
423       void PoolImpl::setTextLocale( const Locale & locale_r )
424       {
425         std::vector<std::string> fallbacklist;
426         for ( Locale l( locale_r ); l != Locale::noCode; 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::setRequestedLocales( const LocaleSet & locales_r )
441       {
442         depSetDirty( "setRequestedLocales" );
443         _requestedLocales = locales_r;
444         MIL << "New RequestedLocales: " << locales_r << endl;
445         _locale_hack( _requestedLocales, _locale2Solver );
446       }
447
448       bool PoolImpl::addRequestedLocale( const Locale & locale_r )
449       {
450         if ( _requestedLocales.insert( locale_r ).second )
451         {
452           depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
453           _locale_hack( _requestedLocales, _locale2Solver );
454           return true;
455         }
456         return false;
457       }
458
459       bool PoolImpl::eraseRequestedLocale( const Locale & locale_r )
460       {
461         if ( _requestedLocales.erase( locale_r ) )
462         {
463           depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
464           _locale_hack( _requestedLocales, _locale2Solver );
465           return true;
466         }
467         return false;
468       }
469
470       static void _getLocaleDeps( Capability cap_r, std::tr1::unordered_set<sat::detail::IdType> & store_r )
471       {
472         // Collect locales from any 'namespace:language(lang)' dependency
473         CapDetail detail( cap_r );
474         if ( detail.kind() == CapDetail::EXPRESSION )
475         {
476           switch ( detail.capRel() )
477           {
478             case CapDetail::CAP_AND:
479             case CapDetail::CAP_OR:
480               // expand
481               _getLocaleDeps( detail.lhs(), store_r );
482               _getLocaleDeps( detail.rhs(), store_r );
483               break;
484
485             case CapDetail::CAP_NAMESPACE:
486               if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
487               {
488                 store_r.insert( detail.rhs().id() );
489               }
490               break;
491
492             case CapDetail::REL_NONE:
493             case CapDetail::CAP_WITH:
494             case CapDetail::CAP_ARCH:
495               break; // unwanted
496           }
497         }
498       }
499
500       const LocaleSet & PoolImpl::getAvailableLocales() const
501       {
502         if ( !_availableLocalesPtr )
503         {
504           // Collect any 'namespace:language(ja)' dependencies
505           std::tr1::unordered_set<sat::detail::IdType> tmp;
506           Pool pool( Pool::instance() );
507           for_( it, pool.solvablesBegin(), pool.solvablesEnd() )
508           {
509             Capabilities cap( it->supplements() );
510             for_( cit, cap.begin(), cap.end() )
511             {
512               _getLocaleDeps( *cit, tmp );
513             }
514           }
515 #warning immediately build LocaleSet as soon as Loale is an Id based type
516           _availableLocalesPtr.reset( new LocaleSet(tmp.size()) );
517           for_( it, tmp.begin(), tmp.end() )
518           {
519             _availableLocalesPtr->insert( Locale( IdString(*it) ) );
520           }
521         }
522         return *_availableLocalesPtr;
523       }
524
525       void PoolImpl::multiversionListInit() const
526       {
527         _multiversionListPtr.reset( new MultiversionList );
528         MultiversionList & multiversionList( *_multiversionListPtr );
529
530         const std::set<std::string> & multiversionSpec( ZConfig::instance().multiversionSpec() );
531         for_( it, multiversionSpec.begin(), multiversionSpec.end() )
532         {
533           static const std::string prefix( "provides:" );
534           if ( str::hasPrefix( *it, prefix ) )
535           {
536             WhatProvides provides( Capability( it->c_str() + prefix.size() ) );
537             if ( provides.empty() )
538             {
539               MIL << "Multiversion install not provided (" << *it << ")" << endl;
540             }
541             else
542             {
543               for_( pit, provides.begin(), provides.end() )
544               {
545                 if ( multiversionList.insert( pit->ident() ).second )
546                   MIL << "Multiversion install " << pit->ident() << " (" << *it << ")" << endl;
547               }
548             }
549           }
550           else
551           {
552             MIL << "Multiversion install " << *it << endl;
553             multiversionList.insert( IdString( *it ) );
554           }
555         }
556       }
557
558       void PoolImpl::onSystemByUserListInit() const
559       {
560         _onSystemByUserListPtr.reset( new OnSystemByUserList );
561         OnSystemByUserList & onSystemByUserList( *_onSystemByUserListPtr );
562
563         Pathname root( ZConfig::instance().systemRoot() );
564         if ( root.empty() )
565         {
566           MIL << "Target not initialized." << endl;
567           return;
568         }
569         PathInfo pi( root / ZConfig::instance().historyLogFile() );
570         MIL << "onSystemByUserList from history: " << pi << endl;
571         if ( ! pi.isFile() )
572           return;
573
574         // go and parse it: 'who' must constain an '@', then it was installed by user request.
575         // 2009-09-29 07:25:19|install|lirc-remotes|0.8.5-3.2|x86_64|root@opensuse|InstallationImage|a204211eb0...
576         std::ifstream infile( pi.path().c_str() );
577         for( iostr::EachLine in( infile ); in; in.next() )
578         {
579           const char * ch( (*in).c_str() );
580           // start with year
581           if ( *ch < '1' || '9' < *ch )
582             continue;
583           const char * sep1 = ::strchr( ch, '|' );      // | after date
584           if ( !sep1 )
585             continue;
586           ++sep1;
587           // if logs an install or delete
588           bool installs = true;
589           if ( ::strncmp( sep1, "install|", 8 ) )
590           {
591             if ( ::strncmp( sep1, "remove |", 8 ) )
592               continue; // no install and no remove
593               else
594                 installs = false; // remove
595           }
596           sep1 += 8;                                    // | after what
597           // get the package name
598           const char * sep2 = ::strchr( sep1, '|' );    // | after name
599           if ( !sep2 || sep1 == sep2 )
600             continue;
601           (*in)[sep2-ch] = '\0';
602           IdString pkg( sep1 );
603           // we're done, if a delete
604           if ( !installs )
605           {
606             onSystemByUserList.erase( pkg );
607             continue;
608           }
609           // now guess whether user installed or not (3rd next field contains 'user@host')
610           if ( (sep1 = ::strchr( sep2+1, '|' ))         // | after version
611             && (sep1 = ::strchr( sep1+1, '|' ))         // | after arch
612             && (sep2 = ::strchr( sep1+1, '|' )) )       // | after who
613           {
614             (*in)[sep2-ch] = '\0';
615             if ( ::strchr( sep1+1, '@' ) )
616             {
617               // by user
618               onSystemByUserList.insert( pkg );
619               continue;
620             }
621           }
622         }
623         MIL << "onSystemByUserList found: " << onSystemByUserList.size() << endl;
624       }
625
626       const std::set<std::string> & PoolImpl::requiredFilesystems() const
627       {
628         if ( ! _requiredFilesystemsPtr )
629         {
630           _requiredFilesystemsPtr.reset( new std::set<std::string> );
631           std::set<std::string> & requiredFilesystems( *_requiredFilesystemsPtr );
632           str::split( base::sysconfig::read( sysconfigStoragePath() )["USED_FS_LIST"],
633                       std::inserter( requiredFilesystems, requiredFilesystems.end() ) );
634         }
635         return *_requiredFilesystemsPtr;
636       }
637
638       /////////////////////////////////////////////////////////////////
639     } // namespace detail
640     ///////////////////////////////////////////////////////////////////
641     /////////////////////////////////////////////////////////////////
642   } // namespace sat
643   ///////////////////////////////////////////////////////////////////
644   /////////////////////////////////////////////////////////////////
645 } // namespace zypp
646 ///////////////////////////////////////////////////////////////////