use LC_MESSAGES instead of LC_CTYPE to determine defaultTextLocale
[platform/upstream/libzypp.git] / zypp / zypp_detail / ZYppImpl.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/zypp_detail/ZYppImpl.cc
10  *
11 */
12
13 #include <sys/utsname.h>
14 #include <unistd.h>
15 #include <iostream>
16 #include <fstream>
17 #include "zypp/TmpPath.h"
18 #include "zypp/base/Logger.h"
19 #include "zypp/base/String.h"
20
21 #include "zypp/zypp_detail/ZYppImpl.h"
22 #include "zypp/solver/detail/Helper.h"
23 #include "zypp/target/TargetImpl.h"
24 #include "zypp/ZYpp.h"
25 #include "zypp/NVRAD.h"
26 #include "zypp/Language.h"
27 #include "zypp/DiskUsageCounter.h"
28 #include "zypp/NameKindProxy.h"
29 #include "zypp/Locks.h"
30
31 using std::endl;
32
33 ///////////////////////////////////////////////////////////////////
34 namespace zypp
35 { /////////////////////////////////////////////////////////////////
36   ///////////////////////////////////////////////////////////////////
37   namespace zypp_detail
38   { /////////////////////////////////////////////////////////////////
39
40     /** The locale to be used for texts and messages.
41      *
42      * For the encoding to be used the preference is
43      *
44      *    LC_ALL, LC_CTYPE, LANG
45      *
46      * For the language of the messages to be used, the preference is
47      *
48      *    LANGUAGE, LC_ALL, LC_MESSAGES, LANG
49      *
50      * Note that LANGUAGE can contain more than one locale name, it can be
51      * a list of locale names like for example
52      *
53      *    LANGUAGE=ja_JP.UTF-8:de_DE.UTF-8:fr_FR.UTF-8
54
55      * \todo Support dynamic fallbacklists defined by LANGUAGE
56      */
57     inline Locale defaultTextLocale()
58     {
59       Locale ret( "en" );
60       const char * envlist[] = { "LC_ALL", "LC_MESSAGES", "LANG", NULL };
61       for ( const char ** envvar = envlist; *envvar; ++envvar )
62         {
63           const char * envlang = getenv( *envvar );
64           if ( envlang )
65             {
66               std::string envstr( envlang );
67               if ( envstr != "POSIX" && envstr != "C" )
68                 {
69                   Locale lang( envlang );
70                   if ( ! lang.code().empty() )
71                     {
72                       ret = lang;
73                       break;
74                     }
75                 }
76             }
77         }
78       return ret;
79     }
80
81     Arch defaultArchitecture()
82     {
83       Arch architecture;
84
85       // detect the true architecture
86       struct utsname buf;
87       if ( uname( &buf ) < 0 )
88         {
89           ERR << "Can't determine system architecture" << endl;
90         }
91       else
92         {
93           architecture = Arch( buf.machine );
94           DBG << "uname architecture is '" << buf.machine << "'" << endl;
95
96           // some CPUs report i686 but dont implement cx8 and cmov
97           // check for both flags in /proc/cpuinfo and downgrade
98           // to i586 if either is missing (cf bug #18885)
99
100           if ( architecture == Arch_i686 )
101             {
102               std::ifstream cpuinfo( "/proc/cpuinfo" );
103               if ( !cpuinfo )
104                 {
105                   ERR << "Cant open /proc/cpuinfo" << endl;
106                 }
107               else
108                 {
109                   char infoline[1024];
110                   while ( cpuinfo.good() )
111                     {
112                       if ( !cpuinfo.getline( infoline, 1024, '\n' ) )
113                         {
114                           if ( cpuinfo.eof() )
115                             break;
116                         }
117                       if ( strncmp( infoline, "flags", 5 ) == 0 )
118                         {
119                           std::string flagsline( infoline );
120                           if ( flagsline.find( "cx8" ) == std::string::npos
121                                || flagsline.find( "cmov" ) == std::string::npos )
122                             {
123                               architecture = Arch_i586;
124                               DBG << "CPU lacks 'cx8' or 'cmov': architecture downgraded to '" << architecture << "'" << endl;
125                             }
126                           break;
127                         } // flags found
128                     } // read proc/cpuinfo
129                 } // proc/cpuinfo opened
130             } // i686 extra flags check
131         }
132
133       if ( getenv( "ZYPP_TESTSUITE_FAKE_ARCH" ) )
134       {
135         architecture = Arch( getenv( "ZYPP_TESTSUITE_FAKE_ARCH" ) );
136         WAR << "ZYPP_TESTSUITE_FAKE_ARCH: Setting fake system architecture for test purpuses to: '" << architecture << "'" << endl;
137       }
138
139       return architecture;
140     }
141     ///////////////////////////////////////////////////////////////////
142     //
143     //  METHOD NAME : ZYppImpl::ZYppImpl
144     //  METHOD TYPE : Constructor
145     //
146     ZYppImpl::ZYppImpl()
147     : _textLocale( defaultTextLocale() )
148     , _pool()
149     , _target(0)
150     , _resolver( new Resolver(_pool.accessor()) )
151     , _architecture( defaultArchitecture() )
152     {
153       MIL << "libzypp: " << VERSION << " built " << __DATE__ << " " <<  __TIME__ << endl;
154       MIL << "defaultTextLocale: '" << _textLocale << "'" << endl;
155       MIL << "System architecture is '" << _architecture << "'" << endl;
156
157       MIL << "initializing keyring..." << std::endl;
158       //_keyring = new KeyRing(homePath() + Pathname("/keyring/all"), homePath() + Pathname("/keyring/trusted"));
159       _keyring = new KeyRing(tmpPath());
160     }
161
162     ///////////////////////////////////////////////////////////////////
163     //
164     //  METHOD NAME : ZYppImpl::~ZYppImpl
165     //  METHOD TYPE : Destructor
166     //
167     ZYppImpl::~ZYppImpl()
168     {}
169
170     //------------------------------------------------------------------------
171     // add/remove resolvables
172
173     void ZYppImpl::addResolvables (const ResStore& store, bool installed)
174     {
175         _pool.insert(store.begin(), store.end(), installed);
176     }
177
178     void ZYppImpl::removeResolvables (const ResStore& store)
179     {
180         for (ResStore::iterator it = store.begin(); it != store.end(); ++it)
181         {
182             _pool.erase(*it);
183         }
184     }
185
186     void ZYppImpl::removeInstalledResolvables ()
187     {
188         for (ResPool::const_iterator it = pool().begin(); it != pool().end();)
189         {
190             ResPool::const_iterator next = it; ++next;
191             if (it->status().isInstalled())
192                 _pool.erase( *it );
193             it = next;
194         }
195     }
196
197     DiskUsageCounter::MountPointSet ZYppImpl::diskUsage()
198     {
199       if ( ! _disk_usage )
200       {
201         setPartitions( DiskUsageCounter::detectMountPoints() );
202       }
203       return _disk_usage->disk_usage(pool());
204     }
205
206     void ZYppImpl::setPartitions(const DiskUsageCounter::MountPointSet &mp)
207     {
208       _disk_usage.reset(new DiskUsageCounter());
209       _disk_usage->setMountPoints(mp);
210     }
211
212     DiskUsageCounter::MountPointSet ZYppImpl::getPartitions() const
213     {
214       if (_disk_usage)
215         return _disk_usage->getMountPoints();
216       else
217         return DiskUsageCounter::detectMountPoints();
218     }
219
220     //------------------------------------------------------------------------
221     // target
222
223     Target_Ptr ZYppImpl::target() const
224     {
225       if (! _target)
226         ZYPP_THROW(Exception("Target not initialized."));
227       return _target;
228      }
229
230     void ZYppImpl::initializeTarget(const Pathname & root)
231     {
232       MIL << "initTarget( " << root << endl;
233       if (_target) {
234         if (_target->root() == root) {
235             MIL << "Repeated call to initializeTarget()" << endl;
236             return;
237         }
238         removeInstalledResolvables( );
239       }
240       _target = new Target( root );
241       _target->enableStorage( root );
242     }
243
244     void ZYppImpl::initTarget(const Pathname & root, bool commit_only)
245     {
246       MIL << "initTarget( " << root << ", " << commit_only << ")" << endl;
247       if (_target) {
248         if (_target->root() == root) {
249           MIL << "Repeated call to initTarget()" << endl;
250           return;
251         }
252         removeInstalledResolvables( );
253       }
254       _target = new Target( root );
255       _target->enableStorage( root );
256       if (!commit_only)
257       {
258         addResolvables( _target->resolvables(), true );
259       }
260     }
261
262
263     void ZYppImpl::finishTarget()
264     {
265       if (_target)
266         removeInstalledResolvables();
267       _target = 0;
268     }
269
270     //------------------------------------------------------------------------
271     // commit
272
273     /** \todo Remove workflow from target, lot's of it could be done here,
274      * and target used for transact. */
275     ZYppCommitResult ZYppImpl::commit( const ZYppCommitPolicy & policy_r )
276     {
277       if ( getenv("ZYPP_TESTSUITE_FAKE_ARCH") )
278       {
279         ZYPP_THROW( Exception("ZYPP_TESTSUITE_FAKE_ARCH set. Commit not allowed and disabled.") );
280       }
281
282       MIL << "Attempt to commit (" << policy_r << ")" << endl;
283       if (! _target)
284         ZYPP_THROW( Exception("Target not initialized.") );
285
286       ZYppCommitResult res = _target->_pimpl->commit( pool(), policy_r );
287
288       if (! policy_r.dryRun() ) {
289         // Tag target data invalid, so they are reloaded on the next call to
290         // target->resolvables(). Actually the target should do this without
291         // foreign help.
292         _target->reset();
293         removeInstalledResolvables();
294         if ( policy_r.syncPoolAfterCommit() )
295           {
296             // reload new status from target
297             addResolvables( _target->resolvables(), true );
298           }
299       }
300
301       MIL << "Commit (" << policy_r << ") returned: "
302           << res << endl;
303       return res;
304     }
305
306     void ZYppImpl::installSrcPackage( const SrcPackage_constPtr & srcPackage_r )
307     {
308       if (! _target)
309         ZYPP_THROW( Exception("Target not initialized.") );
310       _target->_pimpl->installSrcPackage( srcPackage_r );
311     }
312
313     //------------------------------------------------------------------------
314     // locales
315
316     /** */
317     void ZYppImpl::setRequestedLocales( const LocaleSet & locales_r )
318     {
319       ResPool mpool( pool() );
320       // assert all requested are available
321       for ( LocaleSet::const_iterator it = locales_r.begin();
322             it != locales_r.end(); ++it )
323         {
324           NameKindProxy select( nameKindProxy<Language>( mpool, it->code() ) );
325           if ( select.installedEmpty() && select.availableEmpty() )
326             _pool.insert( Language::availableInstance( *it ) );
327         }
328
329       // now adjust status
330       for ( ResPool::byKind_iterator it = mpool.byKindBegin<Language>();
331             it != mpool.byKindEnd<Language>(); ++it )
332         {
333           NameKindProxy select( nameKindProxy<Language>( mpool, (*it)->name() ) );
334           if ( locales_r.find( Locale( (*it)->name() ) ) != locales_r.end() )
335             {
336               // Language is requested
337               if ( select.installedEmpty() )
338                 {
339                   if ( select.availableEmpty() )
340                     {
341                       // no item ==> provide available to install
342                       _pool.insert( Language::availableInstance( Locale((*it)->name()) ) );
343                       select = nameKindProxy<Language>( mpool, (*it)->name() );
344                     }
345                   // available only ==> to install
346                   select.availableBegin()->status().setTransactValue( ResStatus::TRANSACT, ResStatus::USER );
347                 }
348               else
349                 {
350                   // installed ==> keep it
351                   select.installedBegin()->status().setTransactValue( ResStatus::KEEP_STATE, ResStatus::USER );
352                   if ( ! select.availableEmpty() )
353                     {
354                       // both items ==> keep
355                       select.availableBegin()->status().resetTransact( ResStatus::USER );
356                     }
357                 }
358             }
359           else
360             {
361               // Language is NOT requested
362               if ( ! select.installedEmpty() )
363                 select.installedBegin()->status().setTransactValue( ResStatus::TRANSACT, ResStatus::USER );
364               if ( ! select.availableEmpty() )
365                 select.availableBegin()->status().resetTransact( ResStatus::USER );
366             }
367         }
368     }
369
370     /** */
371     ZYppImpl::LocaleSet ZYppImpl::getAvailableLocales() const
372     {
373       ZYpp::LocaleSet ret;
374       ResPool mpool( pool() );
375       for ( ResPool::byKind_iterator it = mpool.byKindBegin<Language>();
376             it != mpool.byKindEnd<Language>(); ++it )
377         {
378           if ( (*it).status().isUninstalled() ) // available!
379             ret.insert( Locale( (*it)->name() ) );
380         }
381       return ret;
382     }
383
384     /** */
385     ZYppImpl::LocaleSet ZYppImpl::getRequestedLocales() const
386     {
387       ZYpp::LocaleSet ret;
388       ResPool mpool( pool() );
389       for ( ResPool::byKind_iterator it = mpool.byKindBegin<Language>();
390             it != mpool.byKindEnd<Language>(); ++it )
391         {
392           NameKindProxy select( nameKindProxy<Language>( mpool, (*it)->name() ) );
393           if ( ! select.installedEmpty()
394                && select.installedBegin()->status().getTransactValue() != ResStatus::TRANSACT )
395             ret.insert( Locale( (*it)->name() ) );
396           else if ( ! select.availableEmpty()
397                     && select.availableBegin()->status().getTransactValue() == ResStatus::TRANSACT )
398             ret.insert( Locale( (*it)->name() ) );
399         }
400       return ret;
401     }
402
403     void ZYppImpl::availableLocale( const Locale & locale_r )
404     {
405       _pool.insert( Language::availableInstance( locale_r ) );
406     }
407
408     //------------------------------------------------------------------------
409     // architecture
410
411     void ZYppImpl::setArchitecture( const Arch & arch )
412     {
413         _architecture = arch;
414         if (_resolver) _resolver->setArchitecture( arch );
415     }
416
417     //------------------------------------------------------------------------
418     // target store path
419
420     Pathname ZYppImpl::homePath() const
421     { return _home_path.empty() ? Pathname("/var/lib/zypp") : _home_path; }
422
423     void ZYppImpl::setHomePath( const Pathname & path )
424     { _home_path = path; }
425
426     Pathname ZYppImpl::tmpPath() const
427     {
428       static TmpDir zypp_tmp_dir( TmpPath::defaultLocation(), "zypp." );
429       return zypp_tmp_dir.path();
430     }
431
432     int ZYppImpl::applyLocks()
433     {
434       Pathname locksrcPath( "/etc/zypp/locks" );
435       try
436       {
437         Target_Ptr trg( target() );
438         if ( trg )
439           locksrcPath = trg->root() / locksrcPath;
440       }
441       catch ( ... )
442       {
443         // noop: Someone decided to let target() throw if the ptr is NULL ;(
444       }
445
446       int num=0;
447       PathInfo locksrc( locksrcPath );
448       if ( locksrc.isFile() )
449       {
450         MIL << "Reading locks from '" << locksrcPath << "'" << endl;
451         num = zypp::locks::readLocks( pool(), locksrcPath );
452         MIL << num << " items locked." << endl;
453       }
454       else
455       {
456         MIL << "No file '" << locksrcPath << "' to read locks from" << endl;
457       }
458       return num;
459     }
460     /******************************************************************
461      **
462      ** FUNCTION NAME : operator<<
463      ** FUNCTION TYPE : std::ostream &
464     */
465     std::ostream & operator<<( std::ostream & str, const ZYppImpl & obj )
466     {
467       return str << "ZYppImpl";
468     }
469
470     /////////////////////////////////////////////////////////////////
471   } // namespace zypp_detail
472   ///////////////////////////////////////////////////////////////////
473   /////////////////////////////////////////////////////////////////
474 } // namespace zypp
475 ///////////////////////////////////////////////////////////////////