8786e78b74021454b3853407182091bf13adbece
[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 satsolver project not providing a common include
39 // directory. (the -devel package does, but the git repo doesn't).
40 // #include <satsolver/repo_helix.h>
41 void 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 sat
54   { /////////////////////////////////////////////////////////////////
55
56     ///////////////////////////////////////////////////////////////////
57     namespace detail
58     { /////////////////////////////////////////////////////////////////
59
60       // MPL checks for satlib constants we redefine to avoid
61       // includes and defines.
62       BOOST_MPL_ASSERT_RELATION( noId,                 ==, STRID_NULL );
63       BOOST_MPL_ASSERT_RELATION( emptyId,              ==, STRID_EMPTY );
64
65       BOOST_MPL_ASSERT_RELATION( noSolvableId,         ==, ID_NULL );
66       BOOST_MPL_ASSERT_RELATION( systemSolvableId,     ==, SYSTEMSOLVABLE );
67
68       BOOST_MPL_ASSERT_RELATION( solvablePrereqMarker, ==, SOLVABLE_PREREQMARKER );
69       BOOST_MPL_ASSERT_RELATION( solvableFileMarker,   ==, SOLVABLE_FILEMARKER );
70
71       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_AND,       ==, REL_AND );
72       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_OR,        ==, REL_OR );
73       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_WITH,      ==, REL_WITH );
74       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_NAMESPACE, ==, REL_NAMESPACE );
75       BOOST_MPL_ASSERT_RELATION( CapDetail::CAP_ARCH,      ==, REL_ARCH );
76
77      /////////////////////////////////////////////////////////////////
78
79       const std::string & PoolImpl::systemRepoAlias()
80       {
81         static const std::string _val( "@System" );
82         return _val;
83       }
84
85       const Pathname & sysconfigStoragePath()
86       {
87         static const Pathname _val( "/etc/sysconfig/storage" );
88         return _val;
89       }
90
91
92       /////////////////////////////////////////////////////////////////
93
94       static void logSat( struct _Pool *, void *data, int type, const char *logString )
95       {
96           if ( type & (SAT_FATAL|SAT_ERROR) ) {
97             _ERR("satsolver") << logString;
98           } else if ( type & SAT_DEBUG_STATS ) {
99             _DBG("satsolver") << logString;
100           } else {
101             _MIL("satsolver") << logString;
102           }
103       }
104
105       detail::IdType PoolImpl::nsCallback( struct _Pool *, void * data, detail::IdType lhs, detail::IdType rhs )
106       {
107         // lhs:    the namespace identifier, e.g. NAMESPACE:MODALIAS
108         // rhs:    the value, e.g. pci:v0000104Cd0000840[01]sv*sd*bc*sc*i*
109         // return: 0 if not supportded
110         //         1 if supported by the system
111         //        -1  AFAIK it's also possible to return a list of solvables that support it, but don't know how.
112
113         static const detail::IdType RET_unsupported    = 0;
114         static const detail::IdType RET_systemProperty = 1;
115         switch ( lhs )
116         {
117           case NAMESPACE_LANGUAGE:
118           {
119             static IdString en( "en" );
120             const std::tr1::unordered_set<IdString> & locale2Solver( reinterpret_cast<PoolImpl*>(data)->_locale2Solver );
121             if ( locale2Solver.empty() )
122             {
123               return rhs == en.id() ? RET_systemProperty : RET_unsupported;
124             }
125             return locale2Solver.find( IdString(rhs) ) != locale2Solver.end() ? RET_systemProperty : RET_unsupported;
126           }
127           break;
128
129           case NAMESPACE_MODALIAS:
130           {
131             // modalias strings in capability may be hexencoded because rpm does not allow
132             // ',', ' ' or other special chars.
133             return target::Modalias::instance().query( str::hexdecode( IdString(rhs).c_str() ) )
134                 ? RET_systemProperty
135               : RET_unsupported;
136           }
137           break;
138
139           case NAMESPACE_FILESYSTEM:
140           {
141             const std::set<std::string> & requiredFilesystems( reinterpret_cast<PoolImpl*>(data)->requiredFilesystems() );
142             return requiredFilesystems.find( IdString(rhs).asString() ) != requiredFilesystems.end() ? RET_systemProperty : RET_unsupported;
143           }
144           break;
145
146           case NAMESPACE_PRODUCTBUDDY:
147           {
148             PoolItem pi( (Solvable(rhs)) );
149             return( pi ? pi.buddy().id() : noId );
150           }
151
152           break;
153         }
154
155         WAR << "Unhandled " << Capability( lhs ) << " vs. " << Capability( rhs ) << endl;
156         return RET_unsupported;
157       }
158
159       ///////////////////////////////////////////////////////////////////
160       //
161       //        METHOD NAME : PoolMember::myPool
162       //        METHOD TYPE : PoolImpl
163       //
164       PoolImpl & PoolMember::myPool()
165       {
166         static PoolImpl _global;
167         return _global;
168       }
169
170       ///////////////////////////////////////////////////////////////////
171       //
172       //        METHOD NAME : PoolImpl::PoolImpl
173       //        METHOD TYPE : Ctor
174       //
175       PoolImpl::PoolImpl()
176       : _pool( ::pool_create() )
177       {
178         MIL << "Creating sat-pool." << endl;
179         if ( ! _pool )
180         {
181           ZYPP_THROW( Exception( _("Can not create sat-pool.") ) );
182         }
183         // initialialize logging
184         if ( getenv("ZYPP_LIBSAT_FULLLOG") )
185           ::pool_setdebuglevel( _pool, 4 );
186         else if ( getenv("ZYPP_FULLLOG") )
187           ::pool_setdebuglevel( _pool, 2 );
188         else
189           ::pool_setdebugmask(_pool, SAT_DEBUG_JOB|SAT_DEBUG_STATS);
190
191         ::pool_setdebugcallback( _pool, logSat, NULL );
192
193         // set namespace callback
194         _pool->nscallback = &nsCallback;
195         _pool->nscallbackdata = (void*)this;
196       }
197
198       ///////////////////////////////////////////////////////////////////
199       //
200       //        METHOD NAME : PoolImpl::~PoolImpl
201       //        METHOD TYPE : Dtor
202       //
203       PoolImpl::~PoolImpl()
204       {
205         ::pool_free( _pool );
206       }
207
208      ///////////////////////////////////////////////////////////////////
209
210       void PoolImpl::setDirty( const char * a1, const char * a2, const char * a3 )
211       {
212         if ( a1 )
213         {
214           if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
215           else if ( a2 ) MIL << a1 << " " << a2 << endl;
216           else           MIL << a1 << endl;
217         }
218         _serial.setDirty();           // pool content change
219         _availableLocalesPtr.reset(); // available locales may change
220         _multiversionListPtr.reset(); // re-evaluate ZConfig::multiversionSpec.
221
222         // invaldate dependency/namespace related indices:
223         depSetDirty();
224       }
225
226       void PoolImpl::depSetDirty( const char * a1, const char * a2, const char * a3 )
227       {
228         if ( a1 )
229         {
230           if      ( a3 ) MIL << a1 << " " << a2 << " " << a3 << endl;
231           else if ( a2 ) MIL << a1 << " " << a2 << endl;
232           else           MIL << a1 << endl;
233         }
234         ::pool_freewhatprovides( _pool );
235       }
236
237       void PoolImpl::prepare() const
238       {
239         if ( _watcher.remember( _serial ) )
240         {
241           // After repo/solvable add/remove:
242           // set pool architecture
243           ::pool_setarch( _pool,  ZConfig::instance().systemArchitecture().asString().c_str() );
244         }
245         if ( ! _pool->whatprovides )
246         {
247           MIL << "pool_createwhatprovides..." << endl;
248
249           ::pool_addfileprovides( _pool );
250           ::pool_createwhatprovides( _pool );
251         }
252         if ( ! _pool->languages )
253         {
254           // initial seting
255           const_cast<PoolImpl*>(this)->setTextLocale( ZConfig::instance().textLocale() );
256         }
257       }
258
259       void PoolImpl::prepareForSolving() const
260       {
261         // additional /etc/sysconfig/storage check:
262         static WatchFile sysconfigFile( sysconfigStoragePath(), WatchFile::NO_INIT );
263         if ( sysconfigFile.hasChanged() )
264         {
265           _requiredFilesystemsPtr.reset(); // recreated on demand
266           const_cast<PoolImpl*>(this)->depSetDirty( "/etc/sysconfig/storage change" );
267         }
268         // finally prepare as usual:
269         prepare();
270       }
271
272       ///////////////////////////////////////////////////////////////////
273
274       ::_Repo * PoolImpl::_createRepo( const std::string & name_r )
275       {
276         setDirty(__FUNCTION__, name_r.c_str() );
277         ::_Repo * ret = ::repo_create( _pool, name_r.c_str() );
278         if ( ret && name_r == systemRepoAlias() )
279           ::pool_set_installed( _pool, ret );
280         return ret;
281       }
282
283       void PoolImpl::_deleteRepo( ::_Repo * repo_r )
284       {
285         setDirty(__FUNCTION__, repo_r->name );
286         ::repo_free( repo_r, /*reuseids*/false );
287         eraseRepoInfo( repo_r );
288         if ( isSystemRepo( repo_r ) )
289         {
290           // systemRepo added
291           _onSystemByUserListPtr.reset(); // re-evaluate
292         }
293       }
294
295       int PoolImpl::_addSolv( ::_Repo * repo_r, FILE * file_r )
296       {
297         setDirty(__FUNCTION__, repo_r->name );
298         int ret = ::repo_add_solv( repo_r , file_r );
299         if ( ret == 0 )
300           _postRepoAdd( repo_r );
301         return ret;
302       }
303
304       int PoolImpl::_addHelix( ::_Repo * repo_r, FILE * file_r )
305       {
306         setDirty(__FUNCTION__, repo_r->name );
307         ::repo_add_helix( repo_r , file_r, 0 ); // unfortunately void
308         _postRepoAdd( repo_r );
309         return 0;
310       }
311
312       void PoolImpl::_postRepoAdd( ::_Repo * repo_r )
313       {
314         if ( ! isSystemRepo( repo_r ) )
315         {
316             // Filter out unwanted archs
317           std::set<detail::IdType> sysids;
318           {
319             Arch::CompatSet sysarchs( Arch::compatSet( ZConfig::instance().systemArchitecture() ) );
320             for_( it, sysarchs.begin(), sysarchs.end() )
321               sysids.insert( it->id() );
322
323               // unfortunately satsolver treats src/nosrc as architecture:
324             sysids.insert( ARCH_SRC );
325             sysids.insert( ARCH_NOSRC );
326           }
327
328           detail::IdType blockBegin = 0;
329           unsigned       blockSize  = 0;
330           for ( detail::IdType i = repo_r->start; i < repo_r->end; ++i )
331           {
332               ::_Solvable * s( _pool->solvables + i );
333               if ( s->repo == repo_r && sysids.find( s->arch ) == sysids.end() )
334               {
335                 // Remember an unwanted arch entry:
336                 if ( ! blockBegin )
337                   blockBegin = i;
338                 ++blockSize;
339               }
340               else if ( blockSize )
341               {
342                 // Free remembered entries
343                   ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
344                   blockBegin = blockSize = 0;
345               }
346           }
347           if ( blockSize )
348           {
349               // Free remembered entries
350               ::repo_free_solvable_block( repo_r, blockBegin, blockSize, /*reuseids*/false );
351               blockBegin = blockSize = 0;
352           }
353         }
354         else
355         {
356           // systemRepo added
357           _onSystemByUserListPtr.reset(); // re-evaluate
358         }
359       }
360
361       detail::SolvableIdType PoolImpl::_addSolvables( ::_Repo * repo_r, unsigned count_r )
362       {
363         setDirty(__FUNCTION__, repo_r->name );
364         return ::repo_add_solvable_block( repo_r, count_r );
365       }
366
367       void PoolImpl::setRepoInfo( RepoIdType id_r, const RepoInfo & info_r )
368       {
369         ::_Repo * repo( getRepo( id_r ) );
370         if ( repo )
371         {
372           bool dirty = false;
373
374           // satsolver priority is based on '<', while yum's repoinfo
375           // uses 1(highest)->99(lowest). Thus we use -info_r.priority.
376           if ( repo->priority != int(-info_r.priority()) )
377           {
378             repo->priority = -info_r.priority();
379             dirty = true;
380           }
381
382           // subpriority is used to e.g. prefer http over dvd iff
383           // both have same priority.
384           int mediaPriority( media::MediaPriority( info_r.url() ) );
385           if ( repo->subpriority != mediaPriority )
386           {
387             repo->subpriority = mediaPriority;
388             dirty = true;
389           }
390
391           if ( dirty )
392             setDirty(__FUNCTION__, info_r.alias().c_str() );
393         }
394         _repoinfos[id_r] = info_r;
395       }
396
397       ///////////////////////////////////////////////////////////////////
398
399       // need on demand and id based Locale
400       void _locale_hack( const LocaleSet & locales_r,
401                          std::tr1::unordered_set<IdString> & locale2Solver )
402       {
403         std::tr1::unordered_set<IdString>( 2*locales_r.size() ).swap( locale2Solver );
404         for_( it, locales_r.begin(),locales_r.end() )
405         {
406           for ( Locale l( *it ); l != Locale::noCode; l = l.fallback() )
407             locale2Solver.insert( IdString( l.code() ) );
408         }
409         MIL << "New Solver Locales: " << locale2Solver << endl;
410       }
411
412       void PoolImpl::setTextLocale( const Locale & locale_r )
413       {
414         std::vector<std::string> fallbacklist;
415         for ( Locale l( locale_r ); l != Locale::noCode; l = l.fallback() )
416         {
417           fallbacklist.push_back( l.code() );
418         }
419         dumpRangeLine( MIL << "pool_set_languages: ", fallbacklist.begin(), fallbacklist.end() ) << endl;
420
421         std::vector<const char *> fallbacklist_cstr;
422         for_( it, fallbacklist.begin(), fallbacklist.end() )
423         {
424           fallbacklist_cstr.push_back( it->c_str() );
425         }
426         ::pool_set_languages( _pool, &fallbacklist_cstr.front(), fallbacklist_cstr.size() );
427       }
428
429       void PoolImpl::setRequestedLocales( const LocaleSet & locales_r )
430       {
431         depSetDirty( "setRequestedLocales" );
432         _requestedLocales = locales_r;
433         MIL << "New RequestedLocales: " << locales_r << endl;
434         _locale_hack( _requestedLocales, _locale2Solver );
435       }
436
437       bool PoolImpl::addRequestedLocale( const Locale & locale_r )
438       {
439         if ( _requestedLocales.insert( locale_r ).second )
440         {
441           depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
442           _locale_hack( _requestedLocales, _locale2Solver );
443           return true;
444         }
445         return false;
446       }
447
448       bool PoolImpl::eraseRequestedLocale( const Locale & locale_r )
449       {
450         if ( _requestedLocales.erase( locale_r ) )
451         {
452           depSetDirty( "addRequestedLocale", locale_r.code().c_str() );
453           _locale_hack( _requestedLocales, _locale2Solver );
454           return true;
455         }
456         return false;
457       }
458
459       static void _getLocaleDeps( Capability cap_r, std::tr1::unordered_set<sat::detail::IdType> & store_r )
460       {
461         // Collect locales from any 'namespace:language(lang)' dependency
462         CapDetail detail( cap_r );
463         if ( detail.kind() == CapDetail::EXPRESSION )
464         {
465           switch ( detail.capRel() )
466           {
467             case CapDetail::CAP_AND:
468             case CapDetail::CAP_OR:
469               // expand
470               _getLocaleDeps( detail.lhs(), store_r );
471               _getLocaleDeps( detail.rhs(), store_r );
472               break;
473
474             case CapDetail::CAP_NAMESPACE:
475               if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
476               {
477                 store_r.insert( detail.rhs().id() );
478               }
479               break;
480
481             case CapDetail::REL_NONE:
482             case CapDetail::CAP_WITH:
483             case CapDetail::CAP_ARCH:
484               break; // unwanted
485           }
486         }
487       }
488
489       const LocaleSet & PoolImpl::getAvailableLocales() const
490       {
491         if ( !_availableLocalesPtr )
492         {
493           // Collect any 'namespace:language(ja)' dependencies
494           std::tr1::unordered_set<sat::detail::IdType> tmp;
495           Pool pool( Pool::instance() );
496           for_( it, pool.solvablesBegin(), pool.solvablesEnd() )
497           {
498             Capabilities cap( it->supplements() );
499             for_( cit, cap.begin(), cap.end() )
500             {
501               _getLocaleDeps( *cit, tmp );
502             }
503           }
504 #warning immediately build LocaleSet as soon as Loale is an Id based type
505           _availableLocalesPtr.reset( new LocaleSet(tmp.size()) );
506           for_( it, tmp.begin(), tmp.end() )
507           {
508             _availableLocalesPtr->insert( Locale( IdString(*it) ) );
509           }
510         }
511         return *_availableLocalesPtr;
512       }
513
514       void PoolImpl::multiversionListInit() const
515       {
516         _multiversionListPtr.reset( new MultiversionList );
517         MultiversionList & multiversionList( *_multiversionListPtr );
518
519         const std::set<std::string> & multiversionSpec( ZConfig::instance().multiversionSpec() );
520         for_( it, multiversionSpec.begin(), multiversionSpec.end() )
521         {
522           static const std::string prefix( "provides:" );
523           if ( str::hasPrefix( *it, prefix ) )
524           {
525             WhatProvides provides( Capability( it->c_str() + prefix.size() ) );
526             if ( provides.empty() )
527             {
528               MIL << "Multiversion install not provided (" << *it << ")" << endl;
529             }
530             else
531             {
532               for_( pit, provides.begin(), provides.end() )
533               {
534                 if ( multiversionList.insert( pit->ident() ).second )
535                   MIL << "Multiversion install " << pit->ident() << " (" << *it << ")" << endl;
536               }
537             }
538           }
539           else
540           {
541             MIL << "Multiversion install " << *it << endl;
542             multiversionList.insert( IdString( *it ) );
543           }
544         }
545       }
546
547       void PoolImpl::onSystemByUserListInit() const
548       {
549         _onSystemByUserListPtr.reset( new OnSystemByUserList );
550         OnSystemByUserList & onSystemByUserList( *_onSystemByUserListPtr );
551
552         Pathname root( ZConfig::instance().systemRoot() );
553         if ( root.empty() )
554         {
555           MIL << "Target not initialized." << endl;
556           return;
557         }
558         PathInfo pi( root / ZConfig::instance().historyLogFile() );
559         MIL << "onSystemByUserList from history: " << pi << endl;
560         if ( ! pi.isFile() )
561           return;
562
563         // go and parse it: 'who' must constain an '@', then it was installed by user request.
564         // 2009-09-29 07:25:19|install|lirc-remotes|0.8.5-3.2|x86_64|root@opensuse|InstallationImage|a204211eb0...
565         std::ifstream infile( pi.path().c_str() );
566         for( iostr::EachLine in( infile ); in; in.next() )
567         {
568           const char * ch( (*in).c_str() );
569           // start with year
570           if ( *ch < '1' || '9' < *ch )
571             continue;
572           const char * sep1 = ::strchr( ch, '|' );      // | after date
573           if ( !sep1 )
574             continue;
575           ++sep1;
576           // if logs an install or delete
577           bool installs = true;
578           if ( ::strncmp( sep1, "install|", 8 ) )
579           {
580             if ( ::strncmp( sep1, "remove |", 8 ) )
581               continue; // no install and no remove
582               else
583                 installs = false; // remove
584           }
585           sep1 += 8;                                    // | after what
586           // get the package name
587           const char * sep2 = ::strchr( sep1, '|' );    // | after name
588           if ( !sep2 || sep1 == sep2 )
589             continue;
590           (*in)[sep2-ch] = '\0';
591           IdString pkg( sep1 );
592           // we're done, if a delete
593           if ( !installs )
594           {
595             onSystemByUserList.erase( pkg );
596             continue;
597           }
598           // now guess whether user installed or not (3rd next field contains 'user@host')
599           if ( (sep1 = ::strchr( sep2+1, '|' ))         // | after version
600             && (sep1 = ::strchr( sep1+1, '|' ))         // | after arch
601             && (sep2 = ::strchr( sep1+1, '|' )) )       // | after who
602           {
603             (*in)[sep2-ch] = '\0';
604             if ( ::strchr( sep1+1, '@' ) )
605             {
606               // by user
607               onSystemByUserList.insert( pkg );
608               continue;
609             }
610           }
611         }
612         MIL << "onSystemByUserList found: " << onSystemByUserList.size() << endl;
613       }
614
615       const std::set<std::string> & PoolImpl::requiredFilesystems() const
616       {
617         if ( ! _requiredFilesystemsPtr )
618         {
619           _requiredFilesystemsPtr.reset( new std::set<std::string> );
620           std::set<std::string> & requiredFilesystems( *_requiredFilesystemsPtr );
621           str::split( base::sysconfig::read( sysconfigStoragePath() )["USED_FS_LIST"],
622                       std::inserter( requiredFilesystems, requiredFilesystems.end() ) );
623         }
624         return *_requiredFilesystemsPtr;
625       }
626
627       /////////////////////////////////////////////////////////////////
628     } // namespace detail
629     ///////////////////////////////////////////////////////////////////
630     /////////////////////////////////////////////////////////////////
631   } // namespace sat
632   ///////////////////////////////////////////////////////////////////
633   /////////////////////////////////////////////////////////////////
634 } // namespace zypp
635 ///////////////////////////////////////////////////////////////////