1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/RepoManager.cc
19 #include "zypp/base/InputStream.h"
20 #include "zypp/base/Logger.h"
21 #include "zypp/base/Gettext.h"
22 #include "zypp/base/Function.h"
23 #include "zypp/base/Regex.h"
24 #include "zypp/PathInfo.h"
25 #include "zypp/TmpPath.h"
27 #include "zypp/repo/RepoException.h"
28 #include "zypp/RepoManager.h"
30 #include "zypp/cache/SolvStore.h"
31 #include "zypp/repo/cached/RepoImpl.h"
32 #include "zypp/media/MediaManager.h"
33 #include "zypp/MediaSetAccess.h"
35 #include "zypp/parser/RepoFileReader.h"
36 #include "zypp/repo/yum/Downloader.h"
37 #include "zypp/parser/yum/RepoParser.h"
38 //#include "zypp/parser/plaindir/RepoParser.h"
39 #include "zypp/repo/susetags/Downloader.h"
40 #include "zypp/parser/susetags/RepoParser.h"
42 #include "zypp/ZYppCallbacks.h"
44 #include "satsolver/pool.h"
45 #include "satsolver/repo_solv.h"
49 using namespace zypp::repo;
50 using namespace zypp::filesystem;
52 using namespace zypp::repo;
54 ///////////////////////////////////////////////////////////////////
56 { /////////////////////////////////////////////////////////////////
58 ///////////////////////////////////////////////////////////////////
60 // CLASS NAME : RepoManagerOptions
62 ///////////////////////////////////////////////////////////////////
64 RepoManagerOptions::RepoManagerOptions()
66 repoCachePath = ZConfig::instance().repoCachePath();
67 repoRawCachePath = ZConfig::instance().repoMetadataPath();
68 knownReposPath = ZConfig::instance().knownReposPath();
71 ////////////////////////////////////////////////////////////////////////////
74 * \short Simple callback to collect the results
76 * Classes like RepoFileParser call the callback
77 * once per each repo in a file.
79 * Passing this functor as callback, you can collect
80 * all resuls at the end, without dealing with async
95 bool collect( const RepoInfo &repo )
97 //MIL << "here in collector: " << repo.alias() << endl;
98 repos.push_back(repo);
99 //MIL << "added: " << repo.alias() << endl;
106 ////////////////////////////////////////////////////////////////////////////
109 * \short Internal version of clean cache
111 * Takes an extra SolvStore reference, so we avoid internally
112 * having 2 SolvStores writing to the same database.
114 static void cleanCacheInternal( cache::SolvStore &store,
115 const RepoInfo &info,
116 const ProgressData::ReceiverFnc & progressrcv = ProgressData::ReceiverFnc() )
118 ProgressData progress;
119 callback::SendReport<ProgressReport> report;
120 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
121 progress.name(str::form(_("Cleaning repository '%s' cache"), info.name().c_str()));
123 if ( !store.isCached(info.alias()) )
126 MIL << info.alias() << " cleaning cache..." << endl;
127 data::RecordId id = store.lookupRepository(info.alias());
129 CombinedProgressData subprogrcv(progress);
131 store.cleanRepository(id, subprogrcv);
134 ////////////////////////////////////////////////////////////////////////////
137 * Reads RepoInfo's from a repo file.
139 * \param file pathname of the file to read.
141 static std::list<RepoInfo> repositories_in_file( const Pathname & file )
143 MIL << "repo file: " << file << endl;
144 RepoCollector collector;
145 parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
146 return collector.repos;
149 ////////////////////////////////////////////////////////////////////////////
151 std::list<RepoInfo> readRepoFile(const Url & repo_file)
153 // no interface to download a specific file, using workaround:
154 //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)
156 Pathname path(url.getPathName());
157 url.setPathName ("/");
158 MediaSetAccess access(url);
159 Pathname local = access.provideFile(path);
161 DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
163 return repositories_in_file(local);
166 ////////////////////////////////////////////////////////////////////////////
169 * \short List of RepoInfo's from a directory
171 * Goes trough every file ending with ".repo" in a directory and adds all
172 * RepoInfo's contained in that file.
174 * \param dir pathname of the directory to read.
176 static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
178 MIL << "directory " << dir << endl;
179 list<RepoInfo> repos;
180 list<Pathname> entries;
181 if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
182 ZYPP_THROW(Exception("failed to read directory"));
184 str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
185 for ( list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
187 if (str::regex_match(it->extension(), allowedRepoExt))
189 list<RepoInfo> tmp = repositories_in_file( *it );
190 repos.insert( repos.end(), tmp.begin(), tmp.end() );
192 //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
193 //MIL << "ok" << endl;
199 ////////////////////////////////////////////////////////////////////////////
201 static void assert_alias( const RepoInfo &info )
203 if (info.alias().empty())
204 ZYPP_THROW(RepoNoAliasException());
207 ////////////////////////////////////////////////////////////////////////////
209 static void assert_urls( const RepoInfo &info )
211 if (info.baseUrlsEmpty())
212 ZYPP_THROW(RepoNoUrlException());
215 ////////////////////////////////////////////////////////////////////////////
218 * \short Calculates the raw cache path for a repository
220 static Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
223 return opt.repoRawCachePath + info.alias();
226 ///////////////////////////////////////////////////////////////////
228 // CLASS NAME : RepoManager::Impl
230 ///////////////////////////////////////////////////////////////////
233 * \short RepoManager implementation.
235 struct RepoManager::Impl
237 Impl( const RepoManagerOptions &opt )
248 RepoManagerOptions options;
251 /** Offer default Impl. */
252 static shared_ptr<Impl> nullimpl()
254 static shared_ptr<Impl> _nullimpl( new Impl );
259 friend Impl * rwcowClone<Impl>( const Impl * rhs );
260 /** clone for RWCOW_pointer */
262 { return new Impl( *this ); }
265 ///////////////////////////////////////////////////////////////////
267 /** \relates RepoManager::Impl Stream output */
268 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
270 return str << "RepoManager::Impl";
273 ///////////////////////////////////////////////////////////////////
275 // CLASS NAME : RepoManager
277 ///////////////////////////////////////////////////////////////////
279 RepoManager::RepoManager( const RepoManagerOptions &opt )
280 : _pimpl( new Impl(opt) )
283 ////////////////////////////////////////////////////////////////////////////
285 RepoManager::~RepoManager()
288 ////////////////////////////////////////////////////////////////////////////
290 std::list<RepoInfo> RepoManager::knownRepositories() const
294 if ( PathInfo(_pimpl->options.knownReposPath).isExist() )
296 RepoInfoList repos = repositories_in_dir(_pimpl->options.knownReposPath);
297 for ( RepoInfoList::iterator it = repos.begin();
301 // set the metadata path for the repo
302 Pathname metadata_path = rawcache_path_for_repoinfo(_pimpl->options, (*it));
303 (*it).setMetadataPath(metadata_path);
308 return std::list<RepoInfo>();
313 ////////////////////////////////////////////////////////////////////////////
315 Pathname RepoManager::metadataPath( const RepoInfo &info ) const
317 return rawcache_path_for_repoinfo(_pimpl->options, info );
320 ////////////////////////////////////////////////////////////////////////////
322 RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const
324 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
325 RepoType repokind = info.type();
328 switch ( repokind.toEnum() )
330 case RepoType::NONE_e:
331 // unknown, probe the local metadata
332 repokind = probe(rawpath.asUrl());
338 switch ( repokind.toEnum() )
340 case RepoType::RPMMD_e :
342 status = RepoStatus( rawpath + "/repodata/repomd.xml");
346 case RepoType::YAST2_e :
348 // the order of RepoStatus && RepoStatus matters! (#304310)
349 status = RepoStatus( rawpath + "/content") && (RepoStatus( rawpath + "/media.1/media"));
353 case RepoType::RPMPLAINDIR_e :
355 if ( PathInfo(Pathname(rawpath + "/cookie")).isExist() )
356 status = RepoStatus( rawpath + "/cookie");
360 case RepoType::NONE_e :
361 // Return default RepoStatus in case of RepoType::NONE
362 // indicating it should be created?
363 // ZYPP_THROW(RepoUnknownTypeException());
369 void RepoManager::touchIndexFile(const RepoInfo & info)
371 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
373 RepoType repokind = info.type();
374 if ( repokind.toEnum() == RepoType::NONE_e )
375 // unknown, probe the local metadata
376 repokind = probe(rawpath.asUrl());
377 // if still unknown, just return
378 if (repokind == RepoType::NONE_e)
382 switch ( repokind.toEnum() )
384 case RepoType::RPMMD_e :
385 p = Pathname(rawpath + "/repodata/repomd.xml");
388 case RepoType::YAST2_e :
389 p = Pathname(rawpath + "/content");
392 case RepoType::RPMPLAINDIR_e :
393 p = Pathname(rawpath + "/cookie");
396 case RepoType::NONE_e :
401 // touch the file, ignore error (they are logged anyway)
402 filesystem::touch(p);
405 bool RepoManager::checkIfToRefreshMetadata( const RepoInfo &info,
407 RawMetadataRefreshPolicy policy )
411 RepoStatus oldstatus;
412 RepoStatus newstatus;
416 MIL << "Going to try to check whether refresh is needed for " << url << endl;
418 repo::RepoType repokind = info.type();
420 // if the type is unknown, try probing.
421 switch ( repokind.toEnum() )
423 case RepoType::NONE_e:
425 repokind = probe(url);
431 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
432 filesystem::assert_dir(rawpath);
433 oldstatus = metadataStatus(info);
435 // now we've got the old (cached) status, we can decide repo.refresh.delay
436 if (policy != RefreshForced)
438 // difference in seconds
439 double diff = difftime(
440 (Date::ValueType)Date::now(),
441 (Date::ValueType)oldstatus.timestamp()) / 60;
443 DBG << "oldstatus: " << (Date::ValueType)oldstatus.timestamp() << endl;
444 DBG << "current time: " << (Date::ValueType)Date::now() << endl;
445 DBG << "last refresh = " << diff << " minutes ago" << endl;
447 if (diff < ZConfig::instance().repo_refresh_delay())
449 MIL << "Repository '" << info.alias()
450 << "' has been refreshed less than repo.refresh.delay ("
451 << ZConfig::instance().repo_refresh_delay()
452 << ") minutes ago. Advising to skip refresh" << endl;
457 // create temp dir as sibling of rawpath
458 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
460 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
461 ( repokind.toEnum() == RepoType::YAST2_e ) )
463 MediaSetAccess media(url);
464 shared_ptr<repo::Downloader> downloader_ptr;
466 if ( repokind.toEnum() == RepoType::RPMMD_e )
467 downloader_ptr.reset(new yum::Downloader(info.path()));
469 downloader_ptr.reset( new susetags::Downloader(info.path()));
471 RepoStatus newstatus = downloader_ptr->status(media);
472 bool refresh = false;
473 if ( oldstatus.checksum() == newstatus.checksum() )
475 MIL << "repo has not changed" << endl;
476 if ( policy == RefreshForced )
478 MIL << "refresh set to forced" << endl;
484 MIL << "repo has changed, going to refresh" << endl;
489 touchIndexFile(info);
494 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
496 RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
497 bool refresh = false;
498 if ( oldstatus.checksum() == newstatus.checksum() )
500 MIL << "repo has not changed" << endl;
501 if ( policy == RefreshForced )
503 MIL << "refresh set to forced" << endl;
509 MIL << "repo has changed, going to refresh" << endl;
514 touchIndexFile(info);
521 ZYPP_THROW(RepoUnknownTypeException());
524 catch ( const Exception &e )
527 ERR << "refresh check failed for " << url << endl;
531 return true; // default
534 void RepoManager::refreshMetadata( const RepoInfo &info,
535 RawMetadataRefreshPolicy policy,
536 const ProgressData::ReceiverFnc & progress )
541 // we will throw this later if no URL checks out fine
542 RepoException rexception(_("Valid metadata not found at specified URL(s)"));
544 // try urls one by one
545 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
551 // check whether to refresh metadata
552 // if the check fails for this url, it throws, so another url will be checked
553 if (!checkIfToRefreshMetadata(info, url, policy))
556 MIL << "Going to refresh metadata from " << url << endl;
558 repo::RepoType repokind = info.type();
560 // if the type is unknown, try probing.
561 switch ( repokind.toEnum() )
563 case RepoType::NONE_e:
565 repokind = probe(*it);
571 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
572 filesystem::assert_dir(rawpath);
574 // create temp dir as sibling of rawpath
575 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
577 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
578 ( repokind.toEnum() == RepoType::YAST2_e ) )
580 MediaSetAccess media(url);
581 shared_ptr<repo::Downloader> downloader_ptr;
583 if ( repokind.toEnum() == RepoType::RPMMD_e )
584 downloader_ptr.reset(new yum::Downloader(info.path()));
586 downloader_ptr.reset( new susetags::Downloader(info.path()));
589 * Given a downloader, sets the other repos raw metadata
590 * path as cache paths for the fetcher, so if another
591 * repo has the same file, it will not download it
592 * but copy it from the other repository
594 std::list<RepoInfo> repos = knownRepositories();
595 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
599 downloader_ptr->addCachePath(rawcache_path_for_repoinfo( _pimpl->options, *it ));
602 downloader_ptr->download( media, tmpdir.path());
605 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
607 RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
609 std::ofstream file(( tmpdir.path() + "/cookie").c_str());
611 ZYPP_THROW (Exception( "Can't open " + tmpdir.path().asString() + "/cookie" ) );
614 file << newstatus.checksum() << endl;
621 ZYPP_THROW(RepoUnknownTypeException());
624 // ok we have the metadata, now exchange
626 TmpDir oldmetadata( TmpDir::makeSibling( rawpath ) );
627 filesystem::rename( rawpath, oldmetadata.path() );
628 // move the just downloaded there
629 filesystem::rename( tmpdir.path(), rawpath );
633 catch ( const Exception &e )
636 ERR << "Trying another url..." << endl;
638 // remember the exception caught for the *first URL*
639 // if all other URLs fail, the rexception will be thrown with the
640 // cause of the problem of the first URL remembered
641 if (it == info.baseUrlsBegin())
642 rexception.remember(e);
645 ERR << "No more urls..." << endl;
646 ZYPP_THROW(rexception);
649 ////////////////////////////////////////////////////////////////////////////
651 void RepoManager::cleanMetadata( const RepoInfo &info,
652 const ProgressData::ReceiverFnc & progressfnc )
654 ProgressData progress(100);
655 progress.sendTo(progressfnc);
657 filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
661 void RepoManager::buildCache( const RepoInfo &info,
662 CacheBuildPolicy policy,
663 const ProgressData::ReceiverFnc & progressrcv )
666 Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
668 cache::SolvStore store(_pimpl->options.repoCachePath);
670 RepoStatus raw_metadata_status = metadataStatus(info);
671 if ( raw_metadata_status.empty() )
673 ZYPP_THROW(RepoMetadataException(info));
676 bool needs_cleaning = false;
677 if ( store.isCached( info.alias() ) )
679 MIL << info.alias() << " is already cached." << endl;
680 data::RecordId id = store.lookupRepository(info.alias());
681 RepoStatus cache_status = store.repositoryStatus(info.alias());
683 if ( cache_status.checksum() == raw_metadata_status.checksum() )
685 MIL << info.alias() << " cache is up to date with metadata." << endl;
686 if ( policy == BuildIfNeeded ) {
690 MIL << info.alias() << " cache rebuild is forced" << endl;
694 needs_cleaning = true;
697 ProgressData progress(100);
698 callback::SendReport<ProgressReport> report;
699 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
700 progress.name(str::form(_("Building repository '%s' cache"), info.name().c_str()));
705 Pathname name = _pimpl->options.repoCachePath;
706 data::RecordId id = store.lookupRepository(info.alias());
709 name += os.str() + ".solv";
711 cleanCacheInternal( store, info);
714 MIL << info.alias() << " building cache..." << endl;
715 data::RecordId id = store.lookupOrAppendRepository(info.alias());
717 repo::RepoType repokind = info.type();
719 // if the type is unknown, try probing.
720 switch ( repokind.toEnum() )
722 case RepoType::NONE_e:
723 // unknown, probe the local metadata
724 repokind = probe(rawpath.asUrl());
731 switch ( repokind.toEnum() )
733 case RepoType::RPMMD_e :
734 case RepoType::YAST2_e :
736 Pathname name = _pimpl->options.repoCachePath;
739 name += os.str() + ".solv";
740 string cmd = "repo2solv.sh \"";
741 cmd += rawpath.asString() + "\" > " + name.asString();
742 int ret = system (cmd.c_str());
743 if (WIFEXITED (ret) && WEXITSTATUS (ret) != 0)
744 ZYPP_THROW(RepoUnknownTypeException());
751 switch ( repokind.toEnum() )
753 case RepoType::RPMMD_e :
756 CombinedProgressData subprogrcv( progress, 100);
757 parser::yum::RepoParser parser(id, store, parser::yum::RepoParserOpts(), subprogrcv);
758 parser.parse(rawpath);
762 case RepoType::YAST2_e :
765 CombinedProgressData subprogrcv( progress, 100);
766 parser::susetags::RepoParser parser(id, store, subprogrcv);
767 parser.parse(rawpath);
772 case RepoType::RPMPLAINDIR_e :
774 CombinedProgressData subprogrcv( progress, 100);
775 InputStream is(rawpath + "cookie");
777 getline( is.stream(), buffer);
779 parser::plaindir::RepoParser parser(id, store, subprogrcv);
780 parser.parse(url.getPathName());
785 ZYPP_THROW(RepoUnknownTypeException());
788 // update timestamp and checksum
789 store.updateRepositoryStatus(id, raw_metadata_status);
791 MIL << "Commit cache.." << endl;
796 ////////////////////////////////////////////////////////////////////////////
798 repo::RepoType RepoManager::probe( const Url &url ) const
800 if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName() ).isDir() )
802 // Handle non existing local directory in advance, as
803 // MediaSetAccess does not support it.
804 return repo::RepoType::NONE;
809 MediaSetAccess access(url);
810 if ( access.doesFileExist("/repodata/repomd.xml") )
811 return repo::RepoType::RPMMD;
812 if ( access.doesFileExist("/content") )
813 return repo::RepoType::YAST2;
815 // if it is a local url of type dir
816 if ( (! media::MediaManager::downloads(url)) && ( url.getScheme() == "dir" ) )
818 Pathname path = Pathname(url.getPathName());
819 if ( PathInfo(path).isDir() )
821 // allow empty dirs for now
822 return repo::RepoType::RPMPLAINDIR;
826 catch ( const media::MediaException &e )
829 RepoException enew("Error trying to read from " + url.asString());
833 catch ( const Exception &e )
836 Exception enew("Unknown error reading from " + url.asString());
841 return repo::RepoType::NONE;
844 ////////////////////////////////////////////////////////////////////////////
846 void RepoManager::cleanCache( const RepoInfo &info,
847 const ProgressData::ReceiverFnc & progressrcv )
849 Pathname name = _pimpl->options.repoCachePath;
850 name += info.alias() + ".solv";
854 ////////////////////////////////////////////////////////////////////////////
856 bool RepoManager::isCached( const RepoInfo &info ) const
858 Pathname name = _pimpl->options.repoCachePath;
859 return PathInfo(name + Pathname(info.alias()).extend(".solv")).isExist();
862 RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
864 Pathname name = _pimpl->options.repoCachePath;
865 return RepoStatus(name + Pathname(info.alias()).extend(".solv"));
868 map<data::RecordId, Repo *> repo2solv;
870 Repository RepoManager::createFromCache( const RepoInfo &info,
871 const ProgressData::ReceiverFnc & progressrcv )
873 callback::SendReport<ProgressReport> report;
874 ProgressData progress;
875 progress.sendTo(ProgressReportAdaptor( progressrcv, report ));
876 //progress.sendTo( progressrcv );
877 progress.name(str::form(_("Reading repository '%s' cache"), info.name().c_str()));
879 //_pimpl->options.repoCachePath
880 if ( ! isCached( info ) )
881 ZYPP_THROW(RepoNotCachedException());
883 MIL << "Repository " << info.alias() << " is cached" << endl;
885 //sat::Pool satpool( sat::Pool::instance() );
887 //Pathname name = _pimpl->options.repoCachePath + Pathname(info.alias()).extend(".solv");
891 //satpool.addRepoSolv(name);
893 catch ( const Exception &e )
898 CombinedProgressData subprogrcv(progress);
900 // repo::cached::RepoOptions opts( info, _pimpl->options.repoCachePath, id );
901 // opts.readingResolvablesProgress = subprogrcv;
902 // //opts.repo = repo;
903 // repo::cached::RepoImpl::Ptr repoimpl =
904 // new repo::cached::RepoImpl( opts );
906 // repoimpl->resolvables();
907 // read the resolvables from cache
908 return Repository::noRepository;
911 ////////////////////////////////////////////////////////////////////////////
914 * Generate a non existing filename in a directory, using a base
915 * name. For example if a directory contains 3 files
921 * If you try to generate a unique filename for this directory,
922 * based on "ruu" you will get "ruu", but if you use the base
923 * "foo" you will get "foo_1"
925 * \param dir Directory where the file needs to be unique
926 * \param basefilename string to base the filename on.
928 static Pathname generate_non_existing_name( const Pathname &dir,
929 const std::string &basefilename )
931 string final_filename = basefilename;
933 while ( PathInfo(dir + final_filename).isExist() )
935 final_filename = basefilename + "_" + str::numstring(counter);
938 return dir + Pathname(final_filename);
941 ////////////////////////////////////////////////////////////////////////////
944 * \short Generate a related filename from a repo info
946 * From a repo info, it will try to use the alias as a filename
947 * escaping it if necessary. Other fallbacks can be added to
948 * this function in case there is no way to use the alias
950 static std::string generate_filename( const RepoInfo &info )
954 std::string filename = info.alias();
955 // replace slashes with underscores
956 size_t pos = filename.find(fnd);
957 while(pos!=string::npos)
959 filename.replace(pos,fnd.length(),rep);
960 pos = filename.find(fnd,pos+rep.length());
962 filename = Pathname(filename).extend(".repo").asString();
963 MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl;
968 ////////////////////////////////////////////////////////////////////////////
970 void RepoManager::addRepository( const RepoInfo &info,
971 const ProgressData::ReceiverFnc & progressrcv )
975 ProgressData progress(100);
976 callback::SendReport<ProgressReport> report;
977 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
978 progress.name(str::form(_("Adding repository '%s'"), info.name().c_str()));
981 std::list<RepoInfo> repos = knownRepositories();
982 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
986 if ( info.alias() == (*it).alias() )
987 ZYPP_THROW(RepoAlreadyExistsException(info.alias()));
990 RepoInfo tosave = info;
992 // check the first url for now
993 if ( ZConfig::instance().repo_add_probe()
994 || ( tosave.type() == RepoType::NONE && tosave.enabled()) )
996 DBG << "unknown repository type, probing" << endl;
999 probedtype = probe(*tosave.baseUrlsBegin());
1000 if ( tosave.baseUrlsSize() > 0 )
1002 if ( probedtype == RepoType::NONE )
1003 ZYPP_THROW(RepoUnknownTypeException());
1005 tosave.setType(probedtype);
1011 // assert the directory exists
1012 filesystem::assert_dir(_pimpl->options.knownReposPath);
1014 Pathname repofile = generate_non_existing_name(_pimpl->options.knownReposPath,
1015 generate_filename(tosave));
1016 // now we have a filename that does not exists
1017 MIL << "Saving repo in " << repofile << endl;
1019 std::ofstream file(repofile.c_str());
1021 ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
1024 tosave.dumpRepoOn(file);
1026 MIL << "done" << endl;
1029 void RepoManager::addRepositories( const Url &url,
1030 const ProgressData::ReceiverFnc & progressrcv )
1032 std::list<RepoInfo> knownrepos = knownRepositories();
1033 std::list<RepoInfo> repos = readRepoFile(url);
1034 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1038 // look if the alias is in the known repos.
1039 for ( std::list<RepoInfo>::const_iterator kit = knownrepos.begin();
1040 kit != knownrepos.end();
1043 if ( (*it).alias() == (*kit).alias() )
1045 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
1046 ZYPP_THROW(RepoAlreadyExistsException((*it).alias()));
1051 string filename = Pathname(url.getPathName()).basename();
1053 if ( filename == Pathname() )
1054 ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
1056 // assert the directory exists
1057 filesystem::assert_dir(_pimpl->options.knownReposPath);
1059 Pathname repofile = generate_non_existing_name(_pimpl->options.knownReposPath, filename);
1060 // now we have a filename that does not exists
1061 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
1063 std::ofstream file(repofile.c_str());
1065 ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
1068 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1072 MIL << "Saving " << (*it).alias() << endl;
1073 (*it).dumpRepoOn(file);
1075 MIL << "done" << endl;
1078 ////////////////////////////////////////////////////////////////////////////
1080 void RepoManager::removeRepository( const RepoInfo & info,
1081 const ProgressData::ReceiverFnc & progressrcv)
1083 ProgressData progress;
1084 callback::SendReport<ProgressReport> report;
1085 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1086 progress.name(str::form(_("Removing repository '%s'"), info.name().c_str()));
1088 MIL << "Going to delete repo " << info.alias() << endl;
1090 std::list<RepoInfo> repos = knownRepositories();
1091 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1095 // they can be the same only if the provided is empty, that means
1096 // the provided repo has no alias
1098 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
1101 // TODO match by url
1103 // we have a matcing repository, now we need to know
1104 // where it does come from.
1105 RepoInfo todelete = *it;
1106 if (todelete.filepath().empty())
1108 ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1112 // figure how many repos are there in the file:
1113 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
1114 if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
1116 // easy, only this one, just delete the file
1117 if ( filesystem::unlink(todelete.filepath()) != 0 )
1119 ZYPP_THROW(RepoException("Can't delete " + todelete.filepath().asString()));
1121 MIL << todelete.alias() << " sucessfully deleted." << endl;
1125 // there are more repos in the same file
1126 // write them back except the deleted one.
1128 //std::ofstream file(tmp.path().c_str());
1130 // assert the directory exists
1131 filesystem::assert_dir(todelete.filepath().dirname());
1133 std::ofstream file(todelete.filepath().c_str());
1135 //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1136 ZYPP_THROW (Exception( "Can't open " + todelete.filepath().asString() ) );
1138 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1139 fit != filerepos.end();
1142 if ( (*fit).alias() != todelete.alias() )
1143 (*fit).dumpRepoOn(file);
1147 CombinedProgressData subprogrcv(progress, 70);
1148 CombinedProgressData cleansubprogrcv(progress, 30);
1149 // now delete it from cache
1150 cleanCache( todelete, subprogrcv);
1151 // now delete metadata (#301037)
1152 cleanMetadata( todelete, cleansubprogrcv);
1153 MIL << todelete.alias() << " sucessfully deleted." << endl;
1155 } // else filepath is empty
1158 // should not be reached on a sucess workflow
1159 ZYPP_THROW(RepoNotFoundException(info));
1162 ////////////////////////////////////////////////////////////////////////////
1164 void RepoManager::modifyRepository( const std::string &alias,
1165 const RepoInfo & newinfo,
1166 const ProgressData::ReceiverFnc & progressrcv )
1168 RepoInfo toedit = getRepositoryInfo(alias);
1170 if (toedit.filepath().empty())
1172 ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1176 // figure how many repos are there in the file:
1177 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
1179 // there are more repos in the same file
1180 // write them back except the deleted one.
1182 //std::ofstream file(tmp.path().c_str());
1184 // assert the directory exists
1185 filesystem::assert_dir(toedit.filepath().dirname());
1187 std::ofstream file(toedit.filepath().c_str());
1189 //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1190 ZYPP_THROW (Exception( "Can't open " + toedit.filepath().asString() ) );
1192 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1193 fit != filerepos.end();
1196 // if the alias is different, dump the original
1197 // if it is the same, dump the provided one
1198 if ( (*fit).alias() != toedit.alias() )
1199 (*fit).dumpRepoOn(file);
1201 newinfo.dumpRepoOn(file);
1206 ////////////////////////////////////////////////////////////////////////////
1208 RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
1209 const ProgressData::ReceiverFnc & progressrcv )
1211 std::list<RepoInfo> repos = knownRepositories();
1212 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1216 if ( (*it).alias() == alias )
1220 info.setAlias(info.alias());
1221 ZYPP_THROW(RepoNotFoundException(info));
1224 ////////////////////////////////////////////////////////////////////////////
1226 RepoInfo RepoManager::getRepositoryInfo( const Url & url,
1227 const url::ViewOption & urlview,
1228 const ProgressData::ReceiverFnc & progressrcv )
1230 std::list<RepoInfo> repos = knownRepositories();
1231 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1235 for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
1236 urlit != (*it).baseUrlsEnd();
1239 if ((*urlit).asString(urlview) == url.asString(urlview))
1244 info.setAlias(info.alias());
1245 info.setBaseUrl(url);
1246 ZYPP_THROW(RepoNotFoundException(info));
1249 ////////////////////////////////////////////////////////////////////////////
1251 std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
1253 return str << *obj._pimpl;
1256 /////////////////////////////////////////////////////////////////
1258 ///////////////////////////////////////////////////////////////////