fix exception
[platform/upstream/libzypp.git] / zypp / RepoManager.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/RepoManager.cc
10  *
11 */
12
13 #include <iostream>
14 #include <list>
15 #include <algorithm>
16 #include "zypp/base/InputStream.h"
17 #include "zypp/base/Logger.h"
18 #include "zypp/base/Function.h"
19 #include "zypp/PathInfo.h"
20 #include "zypp/TmpPath.h"
21
22 #include "zypp/repo/RepoException.h"
23 #include "zypp/RepoManager.h"
24
25 #include "zypp/cache/CacheStore.h"
26 #include "zypp/repo/cached/RepoImpl.h"
27 #include "zypp/MediaSetAccess.h"
28
29 #include "zypp/parser/RepoFileReader.h"
30 #include "zypp/repo/yum/Downloader.h"
31 #include "zypp/parser/yum/RepoParser.h"
32
33 #include "zypp/repo/susetags/Downloader.h"
34 #include "zypp/parser/susetags/RepoParser.h"
35
36 using namespace std;
37 using namespace zypp;
38 using namespace zypp::repo;
39 using namespace zypp::filesystem;
40
41 using namespace zypp::repo;
42
43 ///////////////////////////////////////////////////////////////////
44 namespace zypp
45 { /////////////////////////////////////////////////////////////////
46
47   ///////////////////////////////////////////////////////////////////
48   //
49   //    CLASS NAME : RepoManagerOptions
50   //
51   ///////////////////////////////////////////////////////////////////
52   
53   RepoManagerOptions::RepoManagerOptions()
54   {
55     ZConfig globalConfig;
56     repoCachePath = globalConfig.defaultRepoCachePath();
57     repoRawCachePath = globalConfig.defaultRepoRawCachePath();
58     knownReposPath = globalConfig.defaultKnownReposPath();
59   }
60
61   ////////////////////////////////////////////////////////////////////////////
62
63   /**
64     * \short Simple callback to collect the results
65     *
66     * Classes like RepoFileParser call the callback
67     * once per each repo in a file.
68     *
69     * Passing this functor as callback, you can collect
70     * all resuls at the end, without dealing with async
71     * code.
72     */
73     struct RepoCollector
74     {
75       RepoCollector()
76       {
77         MIL << endl;
78       }
79       
80       ~RepoCollector()
81       {
82         MIL << endl;
83       }
84       
85       bool collect( const RepoInfo &repo )
86       {
87         //MIL << "here in collector: " << repo.alias() << endl;
88         repos.push_back(repo);
89         //MIL << "added: " << repo.alias() << endl;
90         return true;
91       }
92     
93       RepoInfoList repos;
94     };
95  
96   ////////////////////////////////////////////////////////////////////////////
97   
98   /**
99    * Reads RepoInfo's from a repo file.
100    *
101    * \param file pathname of the file to read.
102    */
103   static std::list<RepoInfo> repositories_in_file( const Pathname & file )
104   {
105     MIL << "repo file: " << file << endl;
106     RepoCollector collector;
107     parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
108     return collector.repos;
109   }
110
111   ////////////////////////////////////////////////////////////////////////////
112
113   std::list<RepoInfo> readRepoFile(const Url & repo_file)
114    {
115      // no interface to download a specific file, using workaround:
116      //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)  
117      Url url(repo_file);
118      Pathname path(url.getPathName());
119      url.setPathName ("/");
120      MediaSetAccess access(url);
121      Pathname local = access.provideFile(path);
122   
123      DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
124   
125      return repositories_in_file(local);
126    }
127
128   ////////////////////////////////////////////////////////////////////////////
129   
130   /**
131    * \short List of RepoInfo's from a directory
132    *
133    * Goes trough every file in a directory and adds all
134    * RepoInfo's contained in that file.
135    *
136    * \param dir pathname of the directory to read.
137    */
138   static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
139   {
140     MIL << "directory " << dir << endl;
141     list<RepoInfo> repos;
142     list<Pathname> entries;
143     if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
144       ZYPP_THROW(Exception("failed to read directory"));
145     
146     for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
147     {
148       list<RepoInfo> tmp = repositories_in_file( *it ); 
149       repos.insert( repos.end(), tmp.begin(), tmp.end() );
150
151       //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
152       //MIL << "ok" << endl;
153     }
154     return repos;
155   }
156
157   ////////////////////////////////////////////////////////////////////////////
158   
159   static void assert_alias( const RepoInfo &info )
160   {
161     if (info.alias().empty())
162         ZYPP_THROW(RepoNoAliasException());
163   }
164   
165   ////////////////////////////////////////////////////////////////////////////
166   
167   static void assert_urls( const RepoInfo &info )
168   {
169     if (info.baseUrls().empty())
170         ZYPP_THROW(RepoNoUrlException());
171   }
172   
173   ////////////////////////////////////////////////////////////////////////////
174   
175   /**
176    * \short Calculates the raw cache path for a repository
177    */
178   static Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
179   {
180     assert_alias(info);
181     return opt.repoRawCachePath + info.alias();
182   }
183
184   ///////////////////////////////////////////////////////////////////
185   //
186   //    CLASS NAME : RepoManager::Impl
187   //
188   ///////////////////////////////////////////////////////////////////
189   
190   /**
191    * \short RepoManager implementation.
192    */
193   struct RepoManager::Impl
194   {
195     Impl( const RepoManagerOptions &opt )
196       : options(opt)
197     {
198     
199     }
200     
201     Impl()
202     {
203     
204     }
205     
206     RepoManagerOptions options;
207     
208   public:
209     /** Offer default Impl. */
210     static shared_ptr<Impl> nullimpl()
211     {
212       static shared_ptr<Impl> _nullimpl( new Impl );
213       return _nullimpl;
214     }
215
216   private:
217     friend Impl * rwcowClone<Impl>( const Impl * rhs );
218     /** clone for RWCOW_pointer */
219     Impl * clone() const
220     { return new Impl( *this ); }
221   };
222   ///////////////////////////////////////////////////////////////////
223
224   /** \relates RepoManager::Impl Stream output */
225   inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
226   {
227     return str << "RepoManager::Impl";
228   }
229
230   ///////////////////////////////////////////////////////////////////
231   //
232   //    CLASS NAME : RepoManager
233   //
234   ///////////////////////////////////////////////////////////////////
235
236   RepoManager::RepoManager( const RepoManagerOptions &opt )
237   : _pimpl( new Impl(opt) )
238   {}
239
240   ////////////////////////////////////////////////////////////////////////////
241   
242   RepoManager::~RepoManager()
243   {}
244   
245   ////////////////////////////////////////////////////////////////////////////
246
247   std::list<RepoInfo> RepoManager::knownRepositories() const
248   {
249     MIL << endl;
250     return repositories_in_dir("/etc/zypp/repos.d");
251     MIL << endl;
252   }
253
254   ////////////////////////////////////////////////////////////////////////////
255   
256   void RepoManager::refreshMetadata( const RepoInfo &info,
257                                      const ProgressData::ReceiverFnc & progress )
258   {
259     assert_alias(info);
260     assert_urls(info);
261     
262     // try urls one by one
263     for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
264     {
265       Url url(*it);
266       filesystem::TmpDir tmpdir;
267       
268       repo::RepoType repokind = info.type();
269       
270       // if the type is unknown, try probing.
271       switch ( repokind.toEnum() )
272       {
273         case RepoType::NONE_e:
274           // unknown, probe it
275           repokind = probe(*it);
276         break;
277         default:
278         break;
279       }
280       
281       switch ( repokind.toEnum() )
282       {
283         case RepoType::RPMMD_e :
284         {
285           yum::Downloader downloader( url, "/" );
286           downloader.download(tmpdir.path());
287            // no error
288         }
289         break;
290         case RepoType::YAST2_e :
291         {
292           susetags::Downloader downloader( url, "/" );
293           downloader.download(tmpdir.path());
294           // no error
295         }
296         break;
297         default:
298           ZYPP_THROW(RepoUnknownTypeException());
299       }
300       
301       // ok we have the metadata, now exchange
302       // the contents
303       Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
304       TmpDir oldmetadata;
305       filesystem::assert_dir(rawpath);
306       filesystem::rename( rawpath, oldmetadata.path() );
307       // move the just downloaded there
308       filesystem::rename( tmpdir.path(), rawpath );
309       
310       // we are done.
311     }
312   }
313   
314   ////////////////////////////////////////////////////////////////////////////
315   
316   void RepoManager::cleanMetadata( const RepoInfo &info,
317                                    const ProgressData::ReceiverFnc & progress )
318   {
319     filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
320   }
321   
322   ////////////////////////////////////////////////////////////////////////////
323   
324   void RepoManager::buildCache( const RepoInfo &info,
325                                 const ProgressData::ReceiverFnc & progress )
326   {
327     assert_alias(info);
328     Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
329     
330     cache::CacheStore store(_pimpl->options.repoCachePath);
331     
332     if ( store.isCached( info.alias() ) )
333     {
334       MIL << info.alias() << " is already cached, cleaning..." << endl;
335       data::RecordId id = store.lookupRepository(info.alias());
336       store.cleanRepository(id);
337     }
338     
339     data::RecordId id = store.lookupOrAppendRepository(info.alias());
340     
341     // do we have type?
342     repo::RepoType repokind = info.type();
343       
344       // if the type is unknown, try probing.
345       switch ( repokind.toEnum() )
346       {
347         case RepoType::NONE_e:
348           // unknown, probe the local metadata
349           repokind = probe(Url(rawpath.asString()));
350         break;
351         default:
352         break;
353       }
354       
355       switch ( repokind.toEnum() )
356       {
357         case RepoType::RPMMD_e :
358         {
359           parser::yum::RepoParser parser(id, store);
360           parser.parse(rawpath);
361            // no error
362         }
363         break;
364         case RepoType::YAST2_e :
365         {
366           parser::susetags::RepoParser parser(id, store);
367           parser.parse(rawpath);
368           // no error
369         }
370         break;
371         default:
372           ZYPP_THROW(RepoUnknownTypeException());
373       }
374       
375       MIL << "Commit cache.." << endl;
376       store.commit();
377   }
378   
379   ////////////////////////////////////////////////////////////////////////////
380   
381   repo::RepoType RepoManager::probe( const Url &url )
382   {
383     MediaSetAccess access(url);
384     if ( access.doesFileExist("/repodata/repomd.xml") )
385       return repo::RepoType::RPMMD;
386     if ( access.doesFileExist("/content") )
387       return repo::RepoType::YAST2;
388     
389     return repo::RepoType("UNKNOWN");
390   }
391   
392   ////////////////////////////////////////////////////////////////////////////
393   
394   void RepoManager::cleanCache( const RepoInfo &info,
395                                 const ProgressData::ReceiverFnc & progressrcv )
396   {
397     ProgressData progress;
398     progress.sendTo(progressrcv);
399
400     cache::CacheStore store(_pimpl->options.repoCachePath);
401
402     data::RecordId id = store.lookupRepository(info.alias());
403     store.cleanRepository(id);
404     store.commit();
405   }
406   
407   ////////////////////////////////////////////////////////////////////////////
408   
409   bool RepoManager::isCached( const RepoInfo &info ) const
410   {
411     cache::CacheStore store(_pimpl->options.repoCachePath);
412     return store.isCached(info.alias());
413   }
414   
415   Repository RepoManager::createFromCache( const RepoInfo &info,
416                                            const ProgressData::ReceiverFnc & progress )
417   {
418     cache::CacheStore store(_pimpl->options.repoCachePath);
419     
420     if ( ! store.isCached( info.alias() ) )
421       ZYPP_THROW(RepoNotCachedException());
422     
423     MIL << "Repository " << info.alias() << " is cached" << endl;
424     
425     data::RecordId id = store.lookupRepository(info.alias());
426     repo::cached::RepoImpl::Ptr repoimpl =
427         new repo::cached::RepoImpl( info, _pimpl->options.repoCachePath, id );
428     // read the resolvables from cache
429     repoimpl->createResolvables();
430     return Repository(repoimpl);
431   }
432  
433   ////////////////////////////////////////////////////////////////////////////
434
435   void RepoManager::addRepository( const RepoInfo &info,
436                                    const ProgressData::ReceiverFnc & progressrcv )
437   {
438     assert_alias(info);
439     
440     std::list<RepoInfo> repos = knownRepositories();
441     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
442           it != repos.end();
443           ++it )
444     {
445       if ( info.alias() == (*it).alias() )
446         ZYPP_THROW(RepoAlreadyExistsException(info.alias()));
447     }
448   }
449    
450   void RepoManager::addRepositories( const Url &url,
451                                      const ProgressData::ReceiverFnc & progressrcv )
452   {
453     std::list<RepoInfo> knownrepos = knownRepositories();
454     std::list<RepoInfo> repos = readRepoFile(url);
455     for ( std::list<RepoInfo>::const_iterator it = repos.begin();
456           it != repos.end();
457           ++it )
458     {
459       // look if the alias is in the known repos.
460       for ( std::list<RepoInfo>::const_iterator kit = knownrepos.begin();
461           kit != repos.end();
462           ++kit )
463       {
464         if ( (*it).alias() == (*kit).alias() )
465           ZYPP_THROW(RepoAlreadyExistsException((*it).alias()));
466       }
467     }
468     
469     Pathname filename = Pathname(url.getPathName()).basename();
470     
471     if ( filename == Pathname() )
472       ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
473     
474     // FIXME move this code to a separate function
475     Pathname final_filename = filename;
476     int counter = 1;
477     while ( PathInfo(_pimpl->options.knownReposPath + filename).isExist() )
478     {
479       filename = Pathname( filename.asString() );
480     }
481   }
482   
483   ////////////////////////////////////////////////////////////////////////////
484   
485   std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
486   {
487     return str << *obj._pimpl;
488   }
489
490   /////////////////////////////////////////////////////////////////
491 } // namespace zypp
492 ///////////////////////////////////////////////////////////////////