1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/RepoManager.cc
21 #include "zypp/base/InputStream.h"
22 #include "zypp/base/LogTools.h"
23 #include "zypp/base/Gettext.h"
24 #include "zypp/base/Function.h"
25 #include "zypp/base/Regex.h"
26 #include "zypp/PathInfo.h"
27 #include "zypp/TmpPath.h"
29 #include "zypp/ServiceInfo.h"
30 #include "zypp/repo/RepoException.h"
31 #include "zypp/RepoManager.h"
33 #include "zypp/media/MediaManager.h"
34 #include "zypp/media/CredentialManager.h"
35 #include "zypp/MediaSetAccess.h"
36 #include "zypp/ExternalProgram.h"
37 #include "zypp/ManagedFile.h"
39 #include "zypp/parser/RepoFileReader.h"
40 #include "zypp/parser/ServiceFileReader.h"
41 #include "zypp/parser/RepoindexFileReader.h"
42 #include "zypp/repo/yum/Downloader.h"
43 #include "zypp/repo/susetags/Downloader.h"
44 #include "zypp/parser/plaindir/RepoParser.h"
46 #include "zypp/Target.h" // for Target::targetDistribution() for repo index services
47 #include "zypp/ZYppFactory.h" // to get the Target from ZYpp instance
48 #include "zypp/HistoryLog.h" // to write history :O)
50 #include "zypp/ZYppCallbacks.h"
53 #include "satsolver/pool.h"
54 #include "satsolver/repo.h"
55 #include "satsolver/repo_solv.h"
59 using namespace zypp::repo;
61 ///////////////////////////////////////////////////////////////////
63 { /////////////////////////////////////////////////////////////////
67 /** Check if alias_r is present in repo/service container. */
68 template <class Iterator>
69 inline bool foundAliasIn( const std::string & alias_r, Iterator begin_r, Iterator end_r )
71 for_( it, begin_r, end_r )
72 if ( it->alias() == alias_r )
77 template <class Container>
78 inline bool foundAliasIn( const std::string & alias_r, const Container & cont_r )
79 { return foundAliasIn( alias_r, cont_r.begin(), cont_r.end() ); }
81 /** Find alias_r in repo/service container. */
82 template <class Iterator>
83 inline Iterator findAlias( const std::string & alias_r, Iterator begin_r, Iterator end_r )
85 for_( it, begin_r, end_r )
86 if ( it->alias() == alias_r )
91 template <class Container>
92 inline typename Container::iterator findAlias( const std::string & alias_r, Container & cont_r )
93 { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); }
95 template <class Container>
96 inline typename Container::const_iterator findAlias( const std::string & alias_r, const Container & cont_r )
97 { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); }
100 ///////////////////////////////////////////////////////////////////
102 // CLASS NAME : RepoManagerOptions
104 ///////////////////////////////////////////////////////////////////
106 RepoManagerOptions::RepoManagerOptions( const Pathname & root_r )
108 repoCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoCachePath() );
109 repoRawCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoMetadataPath() );
110 repoSolvCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoSolvfilesPath() );
111 repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() );
112 knownReposPath = Pathname::assertprefix( root_r, ZConfig::instance().knownReposPath() );
113 knownServicesPath = Pathname::assertprefix( root_r, ZConfig::instance().knownServicesPath() );
114 probe = ZConfig::instance().repo_add_probe();
119 RepoManagerOptions RepoManagerOptions::makeTestSetup( const Pathname & root_r )
121 RepoManagerOptions ret;
122 ret.repoCachePath = root_r;
123 ret.repoRawCachePath = root_r/"raw";
124 ret.repoSolvCachePath = root_r/"solv";
125 ret.repoPackagesCachePath = root_r/"packages";
126 ret.knownReposPath = root_r/"repos.d";
127 ret.knownServicesPath = root_r/"services.d";
128 ret.rootDir = root_r;
132 ////////////////////////////////////////////////////////////////////////////
135 * \short Simple callback to collect the results
137 * Classes like RepoFileParser call the callback
138 * once per each repo in a file.
140 * Passing this functor as callback, you can collect
141 * all results at the end, without dealing with async
144 * If targetDistro is set, all repos with non-empty RepoInfo::targetDistribution()
147 * \todo do this through a separate filter
149 struct RepoCollector : private base::NonCopyable
154 RepoCollector(const std::string & targetDistro_)
155 : targetDistro(targetDistro_)
158 bool collect( const RepoInfo &repo )
160 // skip repositories meant for other distros than specified
161 if (!targetDistro.empty()
162 && !repo.targetDistribution().empty()
163 && repo.targetDistribution() != targetDistro)
166 << "Skipping repository meant for '" << targetDistro
167 << "' distribution (current distro is '"
168 << repo.targetDistribution() << "')." << endl;
173 repos.push_back(repo);
178 std::string targetDistro;
181 ////////////////////////////////////////////////////////////////////////////
184 * Reads RepoInfo's from a repo file.
186 * \param file pathname of the file to read.
188 static std::list<RepoInfo> repositories_in_file( const Pathname & file )
190 MIL << "repo file: " << file << endl;
191 RepoCollector collector;
192 parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
193 return collector.repos;
196 ////////////////////////////////////////////////////////////////////////////
199 * \short List of RepoInfo's from a directory
201 * Goes trough every file ending with ".repo" in a directory and adds all
202 * RepoInfo's contained in that file.
204 * \param dir pathname of the directory to read.
206 static std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
208 MIL << "directory " << dir << endl;
209 std::list<RepoInfo> repos;
210 std::list<Pathname> entries;
211 if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
212 ZYPP_THROW(Exception("failed to read directory"));
214 str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
215 for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
217 if (str::regex_match(it->extension(), allowedRepoExt))
219 std::list<RepoInfo> tmp = repositories_in_file( *it );
220 repos.insert( repos.end(), tmp.begin(), tmp.end() );
222 //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos));
223 //MIL << "ok" << endl;
229 ////////////////////////////////////////////////////////////////////////////
231 std::list<RepoInfo> readRepoFile(const Url & repo_file)
233 // no interface to download a specific file, using workaround:
234 //! \todo add MediaManager::provideFile(Url file_url) to easily access any file URLs? (no need for media access id or media_nr)
236 Pathname path(url.getPathName());
237 url.setPathName ("/");
238 MediaSetAccess access(url);
239 Pathname local = access.provideFile(path);
241 DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
243 return repositories_in_file(local);
246 ////////////////////////////////////////////////////////////////////////////
248 inline void assert_alias( const RepoInfo & info )
250 if ( info.alias().empty() )
251 ZYPP_THROW( RepoNoAliasException() );
254 inline void assert_alias( const ServiceInfo & info )
256 if ( info.alias().empty() )
257 ZYPP_THROW( ServiceNoAliasException() );
260 ////////////////////////////////////////////////////////////////////////////
262 inline void assert_urls( const RepoInfo & info )
264 if ( info.baseUrlsEmpty() )
265 ZYPP_THROW( RepoNoUrlException( info ) );
268 inline void assert_url( const ServiceInfo & info )
270 if ( ! info.url().isValid() )
271 ZYPP_THROW( ServiceNoUrlException( info ) );
274 ////////////////////////////////////////////////////////////////////////////
277 * \short Calculates the raw cache path for a repository
279 inline Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
282 return opt.repoRawCachePath / info.escaped_alias();
286 * \short Calculates the packages cache path for a repository
288 inline Pathname packagescache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
291 return opt.repoPackagesCachePath / info.escaped_alias();
295 * \short Calculates the solv cache path for a repository
297 inline Pathname solv_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info)
300 return opt.repoSolvCachePath / info.escaped_alias();
303 ////////////////////////////////////////////////////////////////////////////
305 /** Functor collecting ServiceInfos into a ServiceSet. */
306 class ServiceCollector
309 typedef std::set<ServiceInfo> ServiceSet;
311 ServiceCollector( ServiceSet & services_r )
312 : _services( services_r )
315 bool operator()( const ServiceInfo & service_r ) const
317 _services.insert( service_r );
322 ServiceSet & _services;
325 ////////////////////////////////////////////////////////////////////////////
327 ///////////////////////////////////////////////////////////////////
329 // CLASS NAME : RepoManager::Impl
331 ///////////////////////////////////////////////////////////////////
334 * \short RepoManager implementation.
336 struct RepoManager::Impl
338 Impl( const RepoManagerOptions &opt )
341 init_knownServices();
342 init_knownRepositories();
345 RepoManagerOptions options;
353 void saveService( ServiceInfo & service ) const;
355 Pathname generateNonExistingName( const Pathname &dir,
356 const std::string &basefilename ) const;
358 std::string generateFilename( const RepoInfo & info ) const;
359 std::string generateFilename( const ServiceInfo & info ) const;
363 void init_knownServices();
364 void init_knownRepositories();
367 friend Impl * rwcowClone<Impl>( const Impl * rhs );
368 /** clone for RWCOW_pointer */
370 { return new Impl( *this ); }
373 ///////////////////////////////////////////////////////////////////
375 /** \relates RepoManager::Impl Stream output */
376 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
378 return str << "RepoManager::Impl";
381 ///////////////////////////////////////////////////////////////////
383 void RepoManager::Impl::saveService( ServiceInfo & service ) const
385 filesystem::assert_dir( options.knownServicesPath );
386 Pathname servfile = generateNonExistingName( options.knownServicesPath,
387 generateFilename( service ) );
388 service.setFilepath( servfile );
390 MIL << "saving service in " << servfile << endl;
392 std::ofstream file( servfile.c_str() );
395 ZYPP_THROW( Exception( "Can't open " + servfile.asString() ) );
397 service.dumpAsIniOn( file );
398 MIL << "done" << endl;
402 * Generate a non existing filename in a directory, using a base
403 * name. For example if a directory contains 3 files
409 * If you try to generate a unique filename for this directory,
410 * based on "ruu" you will get "ruu", but if you use the base
411 * "foo" you will get "foo_1"
413 * \param dir Directory where the file needs to be unique
414 * \param basefilename string to base the filename on.
416 Pathname RepoManager::Impl::generateNonExistingName( const Pathname & dir,
417 const std::string & basefilename ) const
419 std::string final_filename = basefilename;
421 while ( PathInfo(dir + final_filename).isExist() )
423 final_filename = basefilename + "_" + str::numstring(counter);
426 return dir + Pathname(final_filename);
429 ////////////////////////////////////////////////////////////////////////////
432 * \short Generate a related filename from a repo info
434 * From a repo info, it will try to use the alias as a filename
435 * escaping it if necessary. Other fallbacks can be added to
436 * this function in case there is no way to use the alias
438 std::string RepoManager::Impl::generateFilename( const RepoInfo & info ) const
440 std::string filename = info.alias();
441 // replace slashes with underscores
442 str::replaceAll( filename, "/", "_" );
444 filename = Pathname(filename).extend(".repo").asString();
445 MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl;
449 std::string RepoManager::Impl::generateFilename( const ServiceInfo & info ) const
451 std::string filename = info.alias();
452 // replace slashes with underscores
453 str::replaceAll( filename, "/", "_" );
455 filename = Pathname(filename).extend(".service").asString();
456 MIL << "generating filename for service [" << info.alias() << "] : '" << filename << "'" << endl;
461 void RepoManager::Impl::init_knownServices()
463 Pathname dir = options.knownServicesPath;
464 std::list<Pathname> entries;
465 if (PathInfo(dir).isExist())
467 if ( filesystem::readdir( entries, Pathname(dir), false ) != 0 )
468 ZYPP_THROW(Exception("failed to read directory"));
470 //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
471 for_(it, entries.begin(), entries.end() )
473 parser::ServiceFileReader(*it, ServiceCollector(services));
478 void RepoManager::Impl::init_knownRepositories()
480 MIL << "start construct known repos" << endl;
482 if ( PathInfo(options.knownReposPath).isExist() )
484 RepoInfoList repol = repositories_in_dir(options.knownReposPath);
485 for ( RepoInfoList::iterator it = repol.begin();
489 // set the metadata path for the repo
490 Pathname metadata_path = rawcache_path_for_repoinfo(options, (*it));
491 (*it).setMetadataPath(metadata_path);
493 // set the downloaded packages path for the repo
494 Pathname packages_path = packagescache_path_for_repoinfo(options, (*it));
495 (*it).setPackagesPath(packages_path);
501 MIL << "end construct known repos" << endl;
504 ///////////////////////////////////////////////////////////////////
506 // CLASS NAME : RepoManager
508 ///////////////////////////////////////////////////////////////////
510 RepoManager::RepoManager( const RepoManagerOptions &opt )
511 : _pimpl( new Impl(opt) )
514 ////////////////////////////////////////////////////////////////////////////
516 RepoManager::~RepoManager()
519 ////////////////////////////////////////////////////////////////////////////
521 bool RepoManager::repoEmpty() const
522 { return _pimpl->repos.empty(); }
524 RepoManager::RepoSizeType RepoManager::repoSize() const
525 { return _pimpl->repos.size(); }
527 RepoManager::RepoConstIterator RepoManager::repoBegin() const
528 { return _pimpl->repos.begin(); }
530 RepoManager::RepoConstIterator RepoManager::repoEnd() const
531 { return _pimpl->repos.end(); }
533 RepoInfo RepoManager::getRepo( const std::string & alias ) const
535 for_( it, repoBegin(), repoEnd() )
536 if ( it->alias() == alias )
538 return RepoInfo::noRepo;
541 bool RepoManager::hasRepo( const std::string & alias ) const
543 for_( it, repoBegin(), repoEnd() )
544 if ( it->alias() == alias )
549 ////////////////////////////////////////////////////////////////////////////
551 Pathname RepoManager::metadataPath( const RepoInfo &info ) const
553 return rawcache_path_for_repoinfo(_pimpl->options, info );
556 Pathname RepoManager::packagesPath( const RepoInfo &info ) const
558 return packagescache_path_for_repoinfo(_pimpl->options, info );
561 ////////////////////////////////////////////////////////////////////////////
563 RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const
565 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
566 RepoType repokind = info.type();
569 switch ( repokind.toEnum() )
571 case RepoType::NONE_e:
572 // unknown, probe the local metadata
573 repokind = probe(rawpath.asUrl());
579 switch ( repokind.toEnum() )
581 case RepoType::RPMMD_e :
583 status = RepoStatus( rawpath + "/repodata/repomd.xml");
587 case RepoType::YAST2_e :
589 status = RepoStatus( rawpath + "/content") && (RepoStatus( rawpath + "/media.1/media"));
593 case RepoType::RPMPLAINDIR_e :
595 if ( PathInfo(Pathname(rawpath + "/cookie")).isExist() )
596 status = RepoStatus( rawpath + "/cookie");
600 case RepoType::NONE_e :
601 // Return default RepoStatus in case of RepoType::NONE
602 // indicating it should be created?
603 // ZYPP_THROW(RepoUnknownTypeException());
609 void RepoManager::touchIndexFile(const RepoInfo & info)
611 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
613 RepoType repokind = info.type();
614 if ( repokind.toEnum() == RepoType::NONE_e )
615 // unknown, probe the local metadata
616 repokind = probe(rawpath.asUrl());
617 // if still unknown, just return
618 if (repokind == RepoType::NONE_e)
622 switch ( repokind.toEnum() )
624 case RepoType::RPMMD_e :
625 p = Pathname(rawpath + "/repodata/repomd.xml");
628 case RepoType::YAST2_e :
629 p = Pathname(rawpath + "/content");
632 case RepoType::RPMPLAINDIR_e :
633 p = Pathname(rawpath + "/cookie");
636 case RepoType::NONE_e :
641 // touch the file, ignore error (they are logged anyway)
642 filesystem::touch(p);
645 RepoManager::RefreshCheckStatus RepoManager::checkIfToRefreshMetadata(
646 const RepoInfo &info,
648 RawMetadataRefreshPolicy policy )
652 RepoStatus oldstatus;
653 RepoStatus newstatus;
657 MIL << "Going to try to check whether refresh is needed for " << url << endl;
659 repo::RepoType repokind = info.type();
661 // if the type is unknown, try probing.
662 switch ( repokind.toEnum() )
664 case RepoType::NONE_e:
666 repokind = probe(url);
672 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
673 filesystem::assert_dir(rawpath);
674 oldstatus = metadataStatus(info);
676 // now we've got the old (cached) status, we can decide repo.refresh.delay
677 if (policy != RefreshForced && policy != RefreshIfNeededIgnoreDelay)
679 // difference in seconds
680 double diff = difftime(
681 (Date::ValueType)Date::now(),
682 (Date::ValueType)oldstatus.timestamp()) / 60;
684 DBG << "oldstatus: " << (Date::ValueType)oldstatus.timestamp() << endl;
685 DBG << "current time: " << (Date::ValueType)Date::now() << endl;
686 DBG << "last refresh = " << diff << " minutes ago" << endl;
688 if (diff < ZConfig::instance().repo_refresh_delay())
690 MIL << "Repository '" << info.alias()
691 << "' has been refreshed less than repo.refresh.delay ("
692 << ZConfig::instance().repo_refresh_delay()
693 << ") minutes ago. Advising to skip refresh" << endl;
694 return REPO_CHECK_DELAYED;
698 // create temp dir as sibling of rawpath
699 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
701 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
702 ( repokind.toEnum() == RepoType::YAST2_e ) )
704 MediaSetAccess media(url);
705 shared_ptr<repo::Downloader> downloader_ptr;
707 if ( repokind.toEnum() == RepoType::RPMMD_e )
708 downloader_ptr.reset(new yum::Downloader(info));
710 downloader_ptr.reset( new susetags::Downloader(info));
712 RepoStatus newstatus = downloader_ptr->status(media);
713 bool refresh = false;
714 if ( oldstatus.checksum() == newstatus.checksum() )
716 MIL << "repo has not changed" << endl;
717 if ( policy == RefreshForced )
719 MIL << "refresh set to forced" << endl;
725 MIL << "repo has changed, going to refresh" << endl;
730 touchIndexFile(info);
732 return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
734 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
736 RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
737 bool refresh = false;
738 if ( oldstatus.checksum() == newstatus.checksum() )
740 MIL << "repo has not changed" << endl;
741 if ( policy == RefreshForced )
743 MIL << "refresh set to forced" << endl;
749 MIL << "repo has changed, going to refresh" << endl;
754 touchIndexFile(info);
756 return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE;
760 ZYPP_THROW(RepoUnknownTypeException(info));
763 catch ( const Exception &e )
766 ERR << "refresh check failed for " << url << endl;
770 return REFRESH_NEEDED; // default
773 void RepoManager::refreshMetadata( const RepoInfo &info,
774 RawMetadataRefreshPolicy policy,
775 const ProgressData::ReceiverFnc & progress )
780 // we will throw this later if no URL checks out fine
781 RepoException rexception(_("Valid metadata not found at specified URL(s)"));
783 // try urls one by one
784 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
790 // check whether to refresh metadata
791 // if the check fails for this url, it throws, so another url will be checked
792 if (checkIfToRefreshMetadata(info, url, policy)!=REFRESH_NEEDED)
795 MIL << "Going to refresh metadata from " << url << endl;
797 repo::RepoType repokind = info.type();
799 // if the type is unknown, try probing.
800 switch ( repokind.toEnum() )
802 case RepoType::NONE_e:
804 repokind = probe(*it);
806 if (repokind.toEnum() != RepoType::NONE_e)
808 // Adjust the probed type in RepoInfo
809 info.setProbedType( repokind ); // lazy init!
810 //save probed type only for repos in system
811 for_( it, repoBegin(), repoEnd() )
813 if ( info.alias() == (*it).alias() )
815 RepoInfo modifiedrepo = info;
816 modifiedrepo.setType( repokind );
817 modifyRepository( info.alias(), modifiedrepo );
827 Pathname rawpath = rawcache_path_for_repoinfo( _pimpl->options, info );
828 filesystem::assert_dir(rawpath);
830 // create temp dir as sibling of rawpath
831 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( rawpath ) );
833 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
834 ( repokind.toEnum() == RepoType::YAST2_e ) )
836 MediaSetAccess media(url);
837 shared_ptr<repo::Downloader> downloader_ptr;
839 MIL << "Creating downloader for [ " << info.name() << " ]" << endl;
841 if ( repokind.toEnum() == RepoType::RPMMD_e )
842 downloader_ptr.reset(new yum::Downloader(info));
844 downloader_ptr.reset( new susetags::Downloader(info) );
847 * Given a downloader, sets the other repos raw metadata
848 * path as cache paths for the fetcher, so if another
849 * repo has the same file, it will not download it
850 * but copy it from the other repository
852 for_( it, repoBegin(), repoEnd() )
854 Pathname cachepath(rawcache_path_for_repoinfo( _pimpl->options, *it ));
855 if ( PathInfo(cachepath).isExist() )
856 downloader_ptr->addCachePath(cachepath);
859 downloader_ptr->download( media, tmpdir.path());
861 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
863 RepoStatus newstatus = parser::plaindir::dirStatus(url.getPathName());
865 std::ofstream file(( tmpdir.path() + "/cookie").c_str());
867 ZYPP_THROW (Exception( "Can't open " + tmpdir.path().asString() + "/cookie" ) );
870 file << newstatus.checksum() << endl;
876 ZYPP_THROW(RepoUnknownTypeException());
879 // ok we have the metadata, now exchange
882 filesystem::TmpDir oldmetadata( filesystem::TmpDir::makeSibling( rawpath ) );
883 filesystem::rename( rawpath, oldmetadata.path() );
884 // move the just downloaded there
885 filesystem::rename( tmpdir.path(), rawpath );
890 catch ( const Exception &e )
893 ERR << "Trying another url..." << endl;
895 // remember the exception caught for the *first URL*
896 // if all other URLs fail, the rexception will be thrown with the
897 // cause of the problem of the first URL remembered
898 if (it == info.baseUrlsBegin())
899 rexception.remember(e);
902 ERR << "No more urls..." << endl;
903 ZYPP_THROW(rexception);
906 ////////////////////////////////////////////////////////////////////////////
908 void RepoManager::cleanMetadata( const RepoInfo &info,
909 const ProgressData::ReceiverFnc & progressfnc )
911 ProgressData progress(100);
912 progress.sendTo(progressfnc);
914 filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info));
918 void RepoManager::cleanPackages( const RepoInfo &info,
919 const ProgressData::ReceiverFnc & progressfnc )
921 ProgressData progress(100);
922 progress.sendTo(progressfnc);
924 filesystem::recursive_rmdir(packagescache_path_for_repoinfo(_pimpl->options, info));
928 void RepoManager::buildCache( const RepoInfo &info,
929 CacheBuildPolicy policy,
930 const ProgressData::ReceiverFnc & progressrcv )
933 Pathname rawpath = rawcache_path_for_repoinfo(_pimpl->options, info);
935 filesystem::assert_dir(_pimpl->options.repoCachePath);
936 RepoStatus raw_metadata_status = metadataStatus(info);
937 if ( raw_metadata_status.empty() )
939 /* if there is no cache at this point, we refresh the raw
940 in case this is the first time - if it's !autorefresh,
941 we may still refresh */
942 refreshMetadata(info, RefreshIfNeeded, progressrcv );
943 raw_metadata_status = metadataStatus(info);
946 bool needs_cleaning = false;
947 if ( isCached( info ) )
949 MIL << info.alias() << " is already cached." << endl;
950 RepoStatus cache_status = cacheStatus(info);
952 if ( cache_status.checksum() == raw_metadata_status.checksum() )
954 MIL << info.alias() << " cache is up to date with metadata." << endl;
955 if ( policy == BuildIfNeeded ) {
959 MIL << info.alias() << " cache rebuild is forced" << endl;
963 needs_cleaning = true;
966 ProgressData progress(100);
967 callback::SendReport<ProgressReport> report;
968 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
969 progress.name(str::form(_("Building repository '%s' cache"), info.name().c_str()));
977 MIL << info.alias() << " building cache..." << endl;
979 Pathname base = solv_path_for_repoinfo( _pimpl->options, info);
980 filesystem::assert_dir(base);
981 Pathname solvfile = base / "solv";
984 repo::RepoType repokind = info.type();
986 // if the type is unknown, try probing.
987 switch ( repokind.toEnum() )
989 case RepoType::NONE_e:
990 // unknown, probe the local metadata
991 repokind = probe(rawpath.asUrl());
997 MIL << "repo type is " << repokind << endl;
999 switch ( repokind.toEnum() )
1001 case RepoType::RPMMD_e :
1002 case RepoType::YAST2_e :
1003 case RepoType::RPMPLAINDIR_e :
1005 // Take care we unlink the solvfile on exception
1006 ManagedFile guard( solvfile, filesystem::unlink );
1008 ExternalProgram::Arguments cmd;
1009 cmd.push_back( "repo2solv.sh" );
1011 // repo2solv expects -o as 1st arg!
1012 cmd.push_back( "-o" );
1013 cmd.push_back( solvfile.asString() );
1015 if ( repokind == RepoType::RPMPLAINDIR )
1016 // FIXME this does only work form dir: URLs
1017 cmd.push_back( info.baseUrlsBegin()->getPathName() );
1019 cmd.push_back( rawpath.asString() );
1021 ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
1022 std::string errdetail;
1024 for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
1025 WAR << " " << output;
1026 if ( errdetail.empty() ) {
1027 errdetail = prog.command();
1030 errdetail += output;
1033 int ret = prog.close();
1036 RepoException ex(str::form("Failed to cache repo (%d).", ret));
1037 ex.remember( errdetail );
1042 guard.resetDispose();
1046 ZYPP_THROW(RepoUnknownTypeException("Unhandled repository type"));
1049 // update timestamp and checksum
1050 setCacheStatus(info, raw_metadata_status);
1051 MIL << "Commit cache.." << endl;
1055 ////////////////////////////////////////////////////////////////////////////
1057 repo::RepoType RepoManager::probe( const Url &url ) const
1059 MIL << "going to probe the type of the repo " << endl;
1061 if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName() ).isDir() )
1063 // Handle non existing local directory in advance, as
1064 // MediaSetAccess does not support it.
1065 return repo::RepoType::NONE;
1068 // prepare exception to be thrown if the type could not be determined
1069 // due to a media exception. We can't throw right away, because of some
1070 // problems with proxy servers returning an incorrect error
1071 // on ftp file-not-found(bnc #335906). Instead we'll check another types
1073 RepoException enew("Error trying to read from " + url.asString());
1074 bool gotMediaException = false;
1077 MediaSetAccess access(url);
1080 if ( access.doesFileExist("/repodata/repomd.xml") )
1081 return repo::RepoType::RPMMD;
1083 catch ( const media::MediaException &e )
1086 DBG << "problem checking for repodata/repomd.xml file" << endl;
1088 gotMediaException = true;
1093 if ( access.doesFileExist("/content") )
1094 return repo::RepoType::YAST2;
1096 catch ( const media::MediaException &e )
1099 DBG << "problem checking for content file" << endl;
1101 gotMediaException = true;
1104 // if it is a local url of type dir
1105 if ( (! media::MediaManager::downloads(url)) && ( url.getScheme() == "dir" ) )
1107 Pathname path = Pathname(url.getPathName());
1108 if ( PathInfo(path).isDir() )
1110 // allow empty dirs for now
1111 return repo::RepoType::RPMPLAINDIR;
1115 catch ( const Exception &e )
1118 Exception enew("Unknown error reading from " + url.asString());
1123 if (gotMediaException)
1126 return repo::RepoType::NONE;
1129 ////////////////////////////////////////////////////////////////////////////
1131 void RepoManager::cleanCache( const RepoInfo &info,
1132 const ProgressData::ReceiverFnc & progressrcv )
1134 ProgressData progress(100);
1135 progress.sendTo(progressrcv);
1138 filesystem::recursive_rmdir(solv_path_for_repoinfo(_pimpl->options, info));
1143 ////////////////////////////////////////////////////////////////////////////
1145 bool RepoManager::isCached( const RepoInfo &info ) const
1147 return PathInfo(solv_path_for_repoinfo( _pimpl->options, info ) / "solv").isExist();
1150 RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
1153 Pathname cookiefile = solv_path_for_repoinfo(_pimpl->options, info) / "cookie";
1155 return RepoStatus::fromCookieFile(cookiefile);
1158 void RepoManager::setCacheStatus( const RepoInfo &info, const RepoStatus &status )
1160 Pathname base = solv_path_for_repoinfo(_pimpl->options, info);
1161 filesystem::assert_dir(base);
1162 Pathname cookiefile = base / "cookie";
1164 status.saveToCookieFile(cookiefile);
1167 void RepoManager::loadFromCache( const RepoInfo & info,
1168 const ProgressData::ReceiverFnc & progressrcv )
1171 Pathname solvfile = solv_path_for_repoinfo(_pimpl->options, info) / "solv";
1173 if ( ! PathInfo(solvfile).isExist() )
1174 ZYPP_THROW(RepoNotCachedException(info));
1178 sat::Pool::instance().addRepoSolv( solvfile, info );
1180 catch ( const Exception & exp )
1183 MIL << "Try to handle exception by rebuilding the solv-file" << endl;
1184 cleanCache( info, progressrcv );
1185 buildCache( info, BuildIfNeeded, progressrcv );
1187 sat::Pool::instance().addRepoSolv( solvfile, info );
1191 ////////////////////////////////////////////////////////////////////////////
1193 void RepoManager::addRepository( const RepoInfo &info,
1194 const ProgressData::ReceiverFnc & progressrcv )
1198 ProgressData progress(100);
1199 callback::SendReport<ProgressReport> report;
1200 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1201 progress.name(str::form(_("Adding repository '%s'"), info.name().c_str()));
1204 RepoInfo tosave = info;
1205 if(_pimpl->repos.find(tosave)!= _pimpl->repos.end())
1206 ZYPP_THROW(RepoAlreadyExistsException(info));
1209 // check the first url for now
1210 if ( _pimpl->options.probe )
1212 DBG << "unknown repository type, probing" << endl;
1214 RepoType probedtype;
1215 probedtype = probe(*tosave.baseUrlsBegin());
1216 if ( tosave.baseUrlsSize() > 0 )
1218 if ( probedtype == RepoType::NONE )
1219 ZYPP_THROW(RepoUnknownTypeException());
1221 tosave.setType(probedtype);
1227 // assert the directory exists
1228 filesystem::assert_dir(_pimpl->options.knownReposPath);
1230 Pathname repofile = _pimpl->generateNonExistingName(
1231 _pimpl->options.knownReposPath, _pimpl->generateFilename(tosave));
1232 // now we have a filename that does not exists
1233 MIL << "Saving repo in " << repofile << endl;
1235 std::ofstream file(repofile.c_str());
1237 ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
1240 tosave.dumpAsIniOn(file);
1241 tosave.setFilepath(repofile);
1242 _pimpl->repos.insert(tosave);
1246 // check for credentials in Urls
1247 bool havePasswords = false;
1248 for_( urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd() )
1249 if ( urlit->hasCredentialsInAuthority() )
1251 havePasswords = true;
1254 // save the credentials
1255 if ( havePasswords )
1257 media::CredentialManager cm(
1258 media::CredManagerOptions(_pimpl->options.rootDir) );
1260 for_(urlit, tosave.baseUrlsBegin(), tosave.baseUrlsEnd())
1261 if (urlit->hasCredentialsInAuthority())
1262 //! \todo use a method calling UI callbacks to ask where to save creds?
1263 cm.saveInUser(media::AuthData(*urlit));
1266 HistoryLog().addRepository(tosave);
1269 MIL << "done" << endl;
1272 void RepoManager::addRepositories( const Url &url,
1273 const ProgressData::ReceiverFnc & progressrcv )
1275 std::list<RepoInfo> repos = readRepoFile(url);
1276 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1280 // look if the alias is in the known repos.
1281 for_ ( kit, repoBegin(), repoEnd() )
1283 if ( (*it).alias() == (*kit).alias() )
1285 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
1286 ZYPP_THROW(RepoAlreadyExistsException(*it));
1291 std::string filename = Pathname(url.getPathName()).basename();
1293 if ( filename == Pathname() )
1294 ZYPP_THROW(RepoException("Invalid repo file name at " + url.asString() ));
1296 // assert the directory exists
1297 filesystem::assert_dir(_pimpl->options.knownReposPath);
1299 Pathname repofile = _pimpl->generateNonExistingName(_pimpl->options.knownReposPath, filename);
1300 // now we have a filename that does not exists
1301 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
1303 std::ofstream file(repofile.c_str());
1305 ZYPP_THROW (Exception( "Can't open " + repofile.asString() ) );
1308 for ( std::list<RepoInfo>::iterator it = repos.begin();
1312 MIL << "Saving " << (*it).alias() << endl;
1313 it->setFilepath(repofile.asString());
1314 it->dumpAsIniOn(file);
1315 _pimpl->repos.insert(*it);
1317 HistoryLog(_pimpl->options.rootDir).addRepository(*it);
1320 MIL << "done" << endl;
1323 ////////////////////////////////////////////////////////////////////////////
1325 void RepoManager::removeRepository( const RepoInfo & info,
1326 const ProgressData::ReceiverFnc & progressrcv)
1328 ProgressData progress;
1329 callback::SendReport<ProgressReport> report;
1330 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1331 progress.name(str::form(_("Removing repository '%s'"), info.name().c_str()));
1333 MIL << "Going to delete repo " << info.alias() << endl;
1335 for_( it, repoBegin(), repoEnd() )
1337 // they can be the same only if the provided is empty, that means
1338 // the provided repo has no alias
1340 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
1343 // TODO match by url
1345 // we have a matcing repository, now we need to know
1346 // where it does come from.
1347 RepoInfo todelete = *it;
1348 if (todelete.filepath().empty())
1350 ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1354 // figure how many repos are there in the file:
1355 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
1356 if ( (filerepos.size() == 1) && ( filerepos.front().alias() == todelete.alias() ) )
1358 // easy, only this one, just delete the file
1359 if ( filesystem::unlink(todelete.filepath()) != 0 )
1361 ZYPP_THROW(RepoException("Can't delete " + todelete.filepath().asString()));
1363 MIL << todelete.alias() << " sucessfully deleted." << endl;
1367 // there are more repos in the same file
1368 // write them back except the deleted one.
1370 //std::ofstream file(tmp.path().c_str());
1372 // assert the directory exists
1373 filesystem::assert_dir(todelete.filepath().dirname());
1375 std::ofstream file(todelete.filepath().c_str());
1377 //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1378 ZYPP_THROW (Exception( "Can't open " + todelete.filepath().asString() ) );
1380 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1381 fit != filerepos.end();
1384 if ( (*fit).alias() != todelete.alias() )
1385 (*fit).dumpAsIniOn(file);
1389 CombinedProgressData subprogrcv(progress, 70);
1390 CombinedProgressData cleansubprogrcv(progress, 30);
1391 // now delete it from cache
1392 if ( isCached(todelete) )
1393 cleanCache( todelete, subprogrcv);
1394 // now delete metadata (#301037)
1395 cleanMetadata( todelete, cleansubprogrcv);
1396 _pimpl->repos.erase(todelete);
1397 MIL << todelete.alias() << " sucessfully deleted." << endl;
1398 HistoryLog(_pimpl->options.rootDir).removeRepository(todelete);
1400 } // else filepath is empty
1403 // should not be reached on a sucess workflow
1404 ZYPP_THROW(RepoNotFoundException(info));
1407 ////////////////////////////////////////////////////////////////////////////
1409 void RepoManager::modifyRepository( const std::string &alias,
1410 const RepoInfo & newinfo,
1411 const ProgressData::ReceiverFnc & progressrcv )
1413 RepoInfo toedit = getRepositoryInfo(alias);
1415 // check if the new alias already exists when renaming the repo
1416 if (alias != newinfo.alias())
1418 for_( it, repoBegin(), repoEnd() )
1420 if ( newinfo.alias() == (*it).alias() )
1421 ZYPP_THROW(RepoAlreadyExistsException(newinfo));
1425 if (toedit.filepath().empty())
1427 ZYPP_THROW(RepoException("Can't figure where the repo is stored"));
1431 // figure how many repos are there in the file:
1432 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
1434 // there are more repos in the same file
1435 // write them back except the deleted one.
1437 //std::ofstream file(tmp.path().c_str());
1439 // assert the directory exists
1440 filesystem::assert_dir(toedit.filepath().dirname());
1442 std::ofstream file(toedit.filepath().c_str());
1444 //ZYPP_THROW (Exception( "Can't open " + tmp.path().asString() ) );
1445 ZYPP_THROW (Exception( "Can't open " + toedit.filepath().asString() ) );
1447 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1448 fit != filerepos.end();
1451 // if the alias is different, dump the original
1452 // if it is the same, dump the provided one
1453 if ( (*fit).alias() != toedit.alias() )
1454 (*fit).dumpAsIniOn(file);
1456 newinfo.dumpAsIniOn(file);
1459 _pimpl->repos.erase(toedit);
1460 _pimpl->repos.insert(newinfo);
1461 HistoryLog(_pimpl->options.rootDir).modifyRepository(toedit, newinfo);
1462 MIL << "repo " << alias << " modified" << endl;
1466 ////////////////////////////////////////////////////////////////////////////
1468 RepoInfo RepoManager::getRepositoryInfo( const std::string &alias,
1469 const ProgressData::ReceiverFnc & progressrcv )
1472 info.setAlias(alias);
1473 RepoConstIterator it = _pimpl->repos.find( info );
1474 if( it == repoEnd() )
1475 ZYPP_THROW(RepoNotFoundException(info));
1480 ////////////////////////////////////////////////////////////////////////////
1482 RepoInfo RepoManager::getRepositoryInfo( const Url & url,
1483 const url::ViewOption & urlview,
1484 const ProgressData::ReceiverFnc & progressrcv )
1486 for_( it, repoBegin(), repoEnd() )
1488 for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin();
1489 urlit != (*it).baseUrlsEnd();
1492 if ((*urlit).asString(urlview) == url.asString(urlview))
1497 info.setBaseUrl(url);
1498 ZYPP_THROW(RepoNotFoundException(info));
1501 ////////////////////////////////////////////////////////////////////////////
1505 ////////////////////////////////////////////////////////////////////////////
1507 bool RepoManager::serviceEmpty() const
1508 { return _pimpl->services.empty(); }
1510 RepoManager::ServiceSizeType RepoManager::serviceSize() const
1511 { return _pimpl->services.size(); }
1513 RepoManager::ServiceConstIterator RepoManager::serviceBegin() const
1514 { return _pimpl->services.begin(); }
1516 RepoManager::ServiceConstIterator RepoManager::serviceEnd() const
1517 { return _pimpl->services.end(); }
1519 ServiceInfo RepoManager::getService( const std::string & alias ) const
1521 for_( it, serviceBegin(), serviceEnd() )
1522 if ( it->alias() == alias )
1524 return ServiceInfo::noService;
1527 bool RepoManager::hasService( const std::string & alias ) const
1529 for_( it, serviceBegin(), serviceEnd() )
1530 if ( it->alias() == alias )
1535 ////////////////////////////////////////////////////////////////////////////
1537 void RepoManager::addService( const std::string & alias, const Url & url )
1539 addService( ServiceInfo(alias, url) );
1542 void RepoManager::addService( const ServiceInfo & service )
1544 assert_alias( service );
1546 // check if service already exists
1547 if ( hasService( service.alias() ) )
1548 ZYPP_THROW( ServiceAlreadyExistsException( service ) );
1550 // Writable ServiceInfo is needed to save the location
1551 // of the .service file. Finaly insert into the service list.
1552 ServiceInfo toSave( service );
1553 _pimpl->saveService( toSave );
1554 _pimpl->services.insert( toSave );
1556 // check for credentials in Url (username:password, not ?credentials param)
1557 if ( toSave.url().hasCredentialsInAuthority() )
1559 media::CredentialManager cm(
1560 media::CredManagerOptions(_pimpl->options.rootDir) );
1562 //! \todo use a method calling UI callbacks to ask where to save creds?
1563 cm.saveInUser(media::AuthData(toSave.url()));
1566 MIL << "added service " << toSave.alias() << endl;
1569 ////////////////////////////////////////////////////////////////////////////
1571 void RepoManager::removeService( const std::string & alias )
1573 MIL << "Going to delete repo " << alias << endl;
1575 const ServiceInfo & service = getService( alias );
1577 Pathname location = service.filepath();
1578 if( location.empty() )
1580 ZYPP_THROW(RepoException("Can't figure where the service is stored"));
1584 parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
1586 // only one service definition in the file
1587 if ( tmpSet.size() == 1 )
1589 if ( filesystem::unlink(location) != 0 )
1591 ZYPP_THROW(RepoException("Can't delete " + location.asString()));
1593 MIL << alias << " sucessfully deleted." << endl;
1597 filesystem::assert_dir(location.dirname());
1599 std::ofstream file(location.c_str());
1601 ZYPP_THROW(Exception("failed open file to write"));
1603 for_(it, tmpSet.begin(), tmpSet.end())
1605 if( it->alias() != alias )
1606 it->dumpAsIniOn(file);
1609 MIL << alias << " sucessfully deleted from file " << location << endl;
1612 // now remove all repositories added by this service
1613 RepoCollector rcollector;
1614 getRepositoriesInService( alias,
1615 boost::make_function_output_iterator(
1616 bind( &RepoCollector::collect, &rcollector, _1 ) ) );
1617 // cannot do this directly in getRepositoriesInService - would invalidate iterators
1618 for_(rit, rcollector.repos.begin(), rcollector.repos.end())
1619 removeRepository(*rit);
1622 void RepoManager::removeService( const ServiceInfo & service )
1623 { removeService(service.alias()); }
1625 ////////////////////////////////////////////////////////////////////////////
1627 void RepoManager::refreshServices()
1629 // copy the set of services since refreshService
1630 // can eventually invalidate the iterator
1631 ServiceSet services( serviceBegin(), serviceEnd() );
1632 for_( it, services.begin(), services.end() )
1634 if ( !it->enabled() )
1637 refreshService(*it);
1641 void RepoManager::refreshService( const ServiceInfo & service )
1642 { refreshService( service.alias() ); }
1644 void RepoManager::refreshService( const std::string & alias )
1646 ServiceInfo service( getService( alias ) );
1647 assert_alias( service );
1648 assert_url( service );
1649 // NOTE: It might be necessary to modify and rewrite the service info.
1650 // Either when probing the type, or when adjusting the repositories
1651 // enable/disable state.:
1652 bool serviceModified = false;
1653 MIL << "going to refresh service '" << service.alias() << "', url: "<< service.url() << endl;
1655 //! \todo add callbacks for apps (start, end, repo removed, repo added, repo changed)
1657 // if the type is unknown, try probing.
1658 if ( service.type() == repo::ServiceType::NONE )
1660 repo::ServiceType type = probeService( service.url() );
1661 if ( type != ServiceType::NONE )
1663 service.setProbedType( type ); // lazy init!
1664 serviceModified = true;
1668 // download the repo index file
1669 media::MediaManager mediamanager;
1670 media::MediaAccessId mid = mediamanager.open( service.url() );
1671 mediamanager.attachDesiredMedia( mid );
1672 mediamanager.provideFile( mid, "repo/repoindex.xml" );
1673 Pathname path = mediamanager.localPath(mid, "repo/repoindex.xml" );
1675 // get target distro identifier
1676 std::string servicesTargetDistro = _pimpl->options.servicesTargetDistro;
1677 if ( servicesTargetDistro.empty() && getZYpp()->getTarget() )
1678 servicesTargetDistro = getZYpp()->target()->targetDistribution();
1679 DBG << "servicesTargetDistro: " << servicesTargetDistro << endl;
1682 RepoCollector collector(servicesTargetDistro);
1683 parser::RepoindexFileReader reader( path, bind( &RepoCollector::collect, &collector, _1 ) );
1684 mediamanager.release( mid );
1685 mediamanager.close( mid );
1688 // set service alias and base url for all collected repositories
1689 for_( it, collector.repos.begin(), collector.repos.end() )
1691 // if the repo url was not set by the repoindex parser, set service's url
1694 if ( it->baseUrlsEmpty() )
1695 url = service.url();
1698 // service repo can contain only one URL now, so no need to iterate.
1699 url = *it->baseUrlsBegin();
1702 // libzypp currently has problem with separate url + path handling
1703 // so just append the path to the baseurl
1704 if ( !it->path().empty() )
1706 Pathname path(url.getPathName());
1708 url.setPathName( path.asString() );
1712 // Prepend service alias:
1713 it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
1716 it->setBaseUrl( url );
1717 // set refrence to the parent service
1718 it->setService( service.alias() );
1721 ////////////////////////////////////////////////////////////////////////////
1722 // Now compare collected repos with the ones in the system...
1724 RepoInfoList oldRepos;
1725 getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) );
1727 // find old repositories to remove...
1728 for_( it, oldRepos.begin(), oldRepos.end() )
1730 if ( ! foundAliasIn( it->alias(), collector.repos ) )
1732 removeRepository( *it );
1736 ////////////////////////////////////////////////////////////////////////////
1737 // create missing repositories and modify exising ones if needed...
1738 for_( it, collector.repos.begin(), collector.repos.end() )
1740 // Service explicitly requests the repo being enabled?
1741 // Service explicitly requests the repo being disabled?
1742 // And hopefully not both ;) If so, enable wins.
1743 bool beEnabled = service.repoToEnableFind( it->alias() );
1744 bool beDisabled = service.repoToDisableFind( it->alias() );
1748 // Remove from enable request list.
1749 // NOTE: repoToDisable is handled differently.
1750 // It gets cleared on each refresh.
1751 service.delRepoToEnable( it->alias() );
1752 serviceModified = true;
1755 RepoInfoList::iterator oldRepo( findAlias( it->alias(), oldRepos ) );
1756 if ( oldRepo == oldRepos.end() )
1758 // Not found in oldRepos ==> a new repo to add
1760 // Make sure the service repo is created with the
1761 // appropriate enable and autorefresh true.
1762 it->setEnabled( beEnabled );
1763 it->setAutorefresh( true );
1765 // At that point check whether a repo with the same alias
1766 // exists outside this service. Maybe forcefully re-alias
1767 // the existing repo?
1768 addRepository( *it );
1770 // save repo credentials
1771 // ma@: task for modifyRepository?
1775 // ==> an exising repo to check
1776 bool oldRepoModified = false;
1780 if ( ! oldRepo->enabled() )
1782 oldRepo->setEnabled( true );
1783 oldRepoModified = true;
1786 else if ( beDisabled )
1788 if ( oldRepo->enabled() )
1790 oldRepo->setEnabled( false );
1791 oldRepoModified = true;
1795 #warning also check changed URL due to PATH/URL change in service, but ignore ?credentials param!
1796 // ma@: task for modifyRepository?
1798 // save if modified:
1799 if ( oldRepoModified )
1801 modifyRepository( oldRepo->alias(), *oldRepo );
1806 // Unlike reposToEnable, reposToDisable is always cleared after refresh.
1807 if ( ! service.reposToDisableEmpty() )
1809 service.clearReposToDisable();
1810 serviceModified = true;
1813 ////////////////////////////////////////////////////////////////////////////
1814 // save service if modified:
1815 if ( serviceModified )
1817 // write out modified service file.
1818 modifyService( service.alias(), service );
1822 ////////////////////////////////////////////////////////////////////////////
1824 void RepoManager::modifyService(const std::string & oldAlias, const ServiceInfo & service)
1826 MIL << "Going to modify service " << oldAlias << endl;
1828 const ServiceInfo & oldService = getService(oldAlias);
1830 Pathname location = oldService.filepath();
1831 if( location.empty() )
1833 ZYPP_THROW(RepoException(
1834 "Cannot figure out where the service file is stored."));
1837 // remember: there may multiple services being defined in one file:
1839 parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
1841 filesystem::assert_dir(location.dirname());
1842 std::ofstream file(location.c_str());
1843 for_(it, tmpSet.begin(), tmpSet.end())
1845 if( *it != oldAlias )
1846 it->dumpAsIniOn(file);
1848 service.dumpAsIniOn(file);
1851 _pimpl->services.erase(oldAlias);
1852 _pimpl->services.insert(service);
1854 // changed properties affecting also repositories
1855 if( oldAlias != service.alias() // changed alias
1856 || oldService.enabled() != service.enabled() // changed enabled status
1859 std::vector<RepoInfo> toModify;
1860 getRepositoriesInService(oldAlias, std::back_inserter(toModify));
1861 for_( it, toModify.begin(), toModify.end() )
1863 if (oldService.enabled() && !service.enabled())
1864 it->setEnabled(false);
1865 else if (!oldService.enabled() && service.enabled())
1867 //! \todo do nothing? the repos will be enabled on service refresh
1868 //! \todo how to know the service needs a (auto) refresh????
1871 it->setService(service.alias());
1872 modifyRepository(it->alias(), *it);
1876 //! \todo refresh the service automatically if url is changed?
1879 ////////////////////////////////////////////////////////////////////////////
1881 repo::ServiceType RepoManager::probeService( const Url &url ) const
1885 MediaSetAccess access(url);
1886 if ( access.doesFileExist("/repo/repoindex.xml") )
1887 return repo::ServiceType::RIS;
1889 catch ( const media::MediaException &e )
1892 RepoException enew("Error trying to read from " + url.asString());
1896 catch ( const Exception &e )
1899 Exception enew("Unknown error reading from " + url.asString());
1904 return repo::ServiceType::NONE;
1908 ////////////////////////////////////////////////////////////////////////////
1912 ////////////////////////////////////////////////////////////////////////////
1914 bool RepoManager::getLicenseFiles(const RepoInfo & repo, const Pathname & target)
1917 for_(url, repo.baseUrlsBegin(), repo.baseUrlsEnd())
1919 // try to get /licenses.tar.gz
1920 MediaSetAccess access(*url);
1921 lpack = access.provideOptionalFile("/license.tar.gz");
1923 // try to get /media.1/license.zip (SLE10, openSUSE10.2 used this)
1926 DBG << "/license.tar.gz not found, trying /media.1/license.zip" << endl;
1927 lpack = access.provideOptionalFile("/media.1/license.zip");
1929 // unpack license.tar.gz to target dir
1932 DBG << "got /license.tar.gz, unpacking..." << endl;
1934 const char* const argv[] =
1939 lpack.asString().c_str(),
1941 target.asString().c_str(),
1945 // execute tar in / (we dont know if there is a tar below _root !)
1946 ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
1949 for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
1952 int ret = tar.close();
1955 ERR << "extracting licenses from tar failed: " << tarmsg << endl;
1964 MIL << "No repository license to confirm for " << repo.alias() << endl;
1967 // unpack license.zip to target dir
1970 DBG << "got /media.1/license.zip, unpacking..." << endl;
1975 // check whether the target dir contains license files
1976 std::list<Pathname> files;
1978 if ( filesystem::readdir( files, target, false ) != 0 )
1980 ERR << "Cannot read the target directory" << endl;
1984 str::regex lfilesregex( "^license\\.?(.*)\\.txt$" );
1985 for_( it, files.begin(), files.end() )
1987 const string & filename = it->basename();
1988 if ( str::regex_match( filename, lfilesregex ) )
1995 ////////////////////////////////////////////////////////////////////////////
1997 Pathname RepoManager::getLicenseFile( const Pathname & ldir )
1999 return getLicenseFile(ldir, ZConfig::instance().defaultTextLocale());
2002 ////////////////////////////////////////////////////////////////////////////
2004 Pathname RepoManager::getLicenseFile( const Pathname & ldir, const Locale & locale )
2006 MIL << "Looking for license file suitable for locale "
2007 << locale << " in directory " << ldir << endl;
2009 std::list<Pathname> files;
2010 std::set<Locale> available;
2012 if ( filesystem::readdir( files, ldir, false ) != 0 )
2014 ERR << "Cannot read the directory." << endl;
2018 str::regex lfilesregex("^license\\.?(.*)\\.txt$");
2019 for_( it, files.begin(), files.end() )
2021 const string & filename = it->basename();
2023 if( str::regex_match(filename, what, lfilesregex) )
2025 Locale l( what[1].empty() ? "" : what[1] );
2026 DBG << "got license file " << *it << ", locale: " << l << endl;
2030 MIL << "got license file " << *it << " for locale " << locale << endl;
2034 available.insert(l);
2037 XXX << "got unknown file: " << it->basename() << endl;
2040 DBG << "No license file found for locale " << locale << endl;
2042 // fall back to language code only
2043 if ( locale.country().hasCode() )
2045 Locale l(locale.language());
2046 std::set<Locale>::const_iterator it = available.find(l);
2047 if ( it != available.end() )
2049 Pathname ret = ldir / ("license."+l.code()+".txt");
2050 MIL << "Got match for locale " << l << ", returning " << ret << endl;
2057 if (PathInfo(ldir/"license.en_US.txt").isFile())
2058 def = ldir/"license.en_US.txt";
2060 if (PathInfo(ldir/"license.en_GB.txt").isFile())
2061 def = ldir/"license.en_GB.txt";
2063 if (PathInfo(ldir/"license.en.txt").isFile())
2064 def = ldir/"license.en.txt";
2065 // check for default
2066 if (PathInfo(ldir/"license.txt").isFile())
2067 def = ldir/"license.txt";
2069 MIL << "Returning '" << def << "'" << endl;
2074 ////////////////////////////////////////////////////////////////////////////
2076 std::set<Locale> getAvailableLicenseLocales( const Pathname & ldir )
2078 return std::set<Locale>();
2079 #warning getAvailableLicenseLocales not yet implemented
2082 // Pathname getInfoFile( const RepoInfo & repo ) media.1/info.txt
2084 ////////////////////////////////////////////////////////////////////////////
2086 std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
2088 return str << *obj._pimpl;
2091 /////////////////////////////////////////////////////////////////
2093 ///////////////////////////////////////////////////////////////////