1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/RepoManager.cc
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"
22 #include "zypp/repo/RepoException.h"
23 #include "zypp/RepoManager.h"
25 #include "zypp/cache/CacheStore.h"
26 #include "zypp/repo/cached/RepoImpl.h"
27 #include "zypp/MediaSetAccess.h"
29 #include "zypp/parser/RepoFileReader.h"
30 #include "zypp/repo/yum/Downloader.h"
31 #include "zypp/parser/yum/RepoParser.h"
33 #include "zypp/repo/susetags/Downloader.h"
34 #include "zypp/parser/susetags/RepoParser.h"
38 using namespace zypp::repo;
39 using namespace zypp::filesystem;
41 using namespace zypp::repo;
43 ///////////////////////////////////////////////////////////////////
45 { /////////////////////////////////////////////////////////////////
47 ///////////////////////////////////////////////////////////////////
49 // CLASS NAME : RepoManagerOptions
51 ///////////////////////////////////////////////////////////////////
53 RepoManagerOptions::RepoManagerOptions()
56 repoCachePath = globalConfig.defaultRepoCachePath();
57 repoRawCachePath = globalConfig.defaultRepoRawCachePath();
58 knownReposPath = globalConfig.defaultKnownReposPath();
61 ////////////////////////////////////////////////////////////////////////////
64 * \short Simple callback to collect the results
66 * Classes like RepoFileParser call the callback
67 * once per each repo in a file.
69 * Passing this functor as callback, you can collect
70 * all resuls at the end, without dealing with async
85 bool collect( const RepoInfo &repo )
87 //MIL << "here in collector: " << repo.alias() << endl;
88 repos.push_back(repo);
89 //MIL << "added: " << repo.alias() << endl;
96 ////////////////////////////////////////////////////////////////////////////
99 * Reads RepoInfo's from a repo file.
101 * \param file pathname of the file to read.
103 static std::list<RepoInfo> repositories_in_file( const Pathname & file )
105 MIL << "repo file: " << file << endl;
106 RepoCollector collector;
107 parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
108 return collector.repos;
111 ////////////////////////////////////////////////////////////////////////////
113 std::list<RepoInfo> readRepoFile(const Url & repo_file)
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)
118 Pathname path(url.getPathName());
119 url.setPathName ("/");
120 MediaSetAccess access(url);
121 Pathname local = access.provideFile(path);
123 DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
125 return repositories_in_file(local);
128 ////////////////////////////////////////////////////////////////////////////
131 * \short List of RepoInfo's from a directory
133 * Goes trough every file in a directory and adds all
134 * RepoInfo's contained in that file.
136 * \param dir pathname of the directory to read.
138 static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
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"));
146 for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
148 list<RepoInfo> tmp = repositories_in_file( *it );
149 repos.insert( repos.end(), tmp.begin(), tmp.end() );
151 //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
152 //MIL << "ok" << endl;
157 ////////////////////////////////////////////////////////////////////////////
159 static void assert_alias( const RepoInfo &info )
161 if (info.alias().empty())
162 ZYPP_THROW(RepoNoAliasException());
165 ////////////////////////////////////////////////////////////////////////////
167 static void assert_urls( const RepoInfo &info )
169 if (info.baseUrls().empty())
170 ZYPP_THROW(RepoNoUrlException());
173 ////////////////////////////////////////////////////////////////////////////
176 * \short Calculates the raw cache path for a repository
178 static Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
181 return opt.repoRawCachePath + info.alias();
184 ///////////////////////////////////////////////////////////////////
186 // CLASS NAME : RepoManager::Impl
188 ///////////////////////////////////////////////////////////////////
191 * \short RepoManager implementation.
193 struct RepoManager::Impl
195 Impl( const RepoManagerOptions &opt )
206 RepoManagerOptions options;
209 /** Offer default Impl. */
210 static shared_ptr<Impl> nullimpl()
212 static shared_ptr<Impl> _nullimpl( new Impl );
217 friend Impl * rwcowClone<Impl>( const Impl * rhs );
218 /** clone for RWCOW_pointer */
220 { return new Impl( *this ); }
222 ///////////////////////////////////////////////////////////////////
224 /** \relates RepoManager::Impl Stream output */
225 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
227 return str << "RepoManager::Impl";
230 ///////////////////////////////////////////////////////////////////
232 // CLASS NAME : RepoManager
234 ///////////////////////////////////////////////////////////////////
236 RepoManager::RepoManager( const RepoManagerOptions &opt )
237 : _pimpl( new Impl(opt) )
240 ////////////////////////////////////////////////////////////////////////////
242 RepoManager::~RepoManager()
245 ////////////////////////////////////////////////////////////////////////////
247 std::list<RepoInfo> RepoManager::knownRepositories() const
250 return repositories_in_dir("/etc/zypp/repos.d");
254 ////////////////////////////////////////////////////////////////////////////
256 void RepoManager::refreshMetadata( const RepoInfo &info,
257 const ProgressData::ReceiverFnc & progress )
262 // try urls one by one
263 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
266 filesystem::TmpDir tmpdir;
268 repo::RepoType repokind = info.type();
270 // if the type is unknown, try probing.
271 switch ( repokind.toEnum() )
273 case RepoType::NONE_e:
275 repokind = probe(*it);
281 switch ( repokind.toEnum() )
283 case RepoType::RPMMD_e :
285 yum::Downloader downloader( url, "/" );
286 downloader.download(tmpdir.path());
290 case RepoType::YAST2_e :
292 susetags::Downloader downloader( url, "/" );
293 downloader.download(tmpdir.path());
298 ZYPP_THROW(RepoUnknownTypeException());
301 // ok we have the metadata, now exchange
303 Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
305 filesystem::assert_dir(rawpath);
306 filesystem::rename( rawpath, oldmetadata.path() );
307 // move the just downloaded there
308 filesystem::rename( tmpdir.path(), rawpath );
314 ////////////////////////////////////////////////////////////////////////////
316 void RepoManager::cleanMetadata( const RepoInfo &info,
317 const ProgressData::ReceiverFnc & progress )
319 filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
322 ////////////////////////////////////////////////////////////////////////////
324 void RepoManager::buildCache( const RepoInfo &info,
325 const ProgressData::ReceiverFnc & progress )
328 Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
330 cache::CacheStore store(_pimpl->options.repoCachePath);
332 if ( store.isCached( info.alias() ) )
334 MIL << info.alias() << " is already cached, cleaning..." << endl;
335 data::RecordId id = store.lookupRepository(info.alias());
336 store.cleanRepository(id);
339 data::RecordId id = store.lookupOrAppendRepository(info.alias());
342 repo::RepoType repokind = info.type();
344 // if the type is unknown, try probing.
345 switch ( repokind.toEnum() )
347 case RepoType::NONE_e:
348 // unknown, probe the local metadata
349 repokind = probe(Url(rawpath.asString()));
355 switch ( repokind.toEnum() )
357 case RepoType::RPMMD_e :
359 parser::yum::RepoParser parser(id, store);
360 parser.parse(rawpath);
364 case RepoType::YAST2_e :
366 parser::susetags::RepoParser parser(id, store);
367 parser.parse(rawpath);
372 ZYPP_THROW(RepoUnknownTypeException());
375 MIL << "Commit cache.." << endl;
379 ////////////////////////////////////////////////////////////////////////////
381 repo::RepoType RepoManager::probe( const Url &url )
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;
389 return repo::RepoType("UNKNOWN");
392 ////////////////////////////////////////////////////////////////////////////
394 void RepoManager::cleanCache( const RepoInfo &info,
395 const ProgressData::ReceiverFnc & progressrcv )
397 ProgressData progress;
398 progress.sendTo(progressrcv);
400 cache::CacheStore store(_pimpl->options.repoCachePath);
402 data::RecordId id = store.lookupRepository(info.alias());
403 store.cleanRepository(id);
407 ////////////////////////////////////////////////////////////////////////////
409 bool RepoManager::isCached( const RepoInfo &info ) const
411 cache::CacheStore store(_pimpl->options.repoCachePath);
412 return store.isCached(info.alias());
415 Repository RepoManager::createFromCache( const RepoInfo &info,
416 const ProgressData::ReceiverFnc & progress )
418 cache::CacheStore store(_pimpl->options.repoCachePath);
420 if ( ! store.isCached( info.alias() ) )
421 ZYPP_THROW(RepoNotCachedException());
423 MIL << "Repository " << info.alias() << " is cached" << endl;
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);
433 ////////////////////////////////////////////////////////////////////////////
435 void RepoManager::addRepository( const RepoInfo &info,
436 const ProgressData::ReceiverFnc & progressrcv )
440 std::list<RepoInfo> repos = knownRepositories();
441 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
445 if ( info.alias() == (*it).alias() )
446 ZYPP_THROW(RepoAlreadyExistsException(info.alias()));
450 void RepoManager::addRepositories( const Url &url,
451 const ProgressData::ReceiverFnc & progressrcv )
453 std::list<RepoInfo> knownrepos = knownRepositories();
454 std::list<RepoInfo> repos = readRepoFile(url);
455 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
459 // look if the alias is in the known repos.
460 for ( std::list<RepoInfo>::const_iterator kit = knownrepos.begin();
464 if ( (*it).alias() == (*kit).alias() )
465 ZYPP_THROW(RepoAlreadyExistsException((*it).alias()));
469 Pathname filename = Pathname(url.getPathName()).basename();
471 if ( filename == Pathname() )
472 ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
474 // FIXME move this code to a separate function
475 Pathname final_filename = filename;
477 while ( PathInfo(_pimpl->options.knownReposPath + filename).isExist() )
479 filename = Pathname( filename.asString() );
483 ////////////////////////////////////////////////////////////////////////////
485 std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
487 return str << *obj._pimpl;
490 /////////////////////////////////////////////////////////////////
492 ///////////////////////////////////////////////////////////////////