libzypp 9.41.1
|
00001 /*---------------------------------------------------------------------\ 00002 | ____ _ __ __ ___ | 00003 | |__ / \ / / . \ . \ | 00004 | / / \ V /| _/ _/ | 00005 | / /__ | | | | | | | 00006 | /_____||_| |_| |_| | 00007 | | 00008 \---------------------------------------------------------------------*/ 00013 #include <cstdlib> 00014 #include <iostream> 00015 #include <fstream> 00016 #include <sstream> 00017 #include <list> 00018 #include <map> 00019 #include <algorithm> 00020 00021 #include "zypp/base/InputStream.h" 00022 #include "zypp/base/LogTools.h" 00023 #include "zypp/base/Gettext.h" 00024 #include "zypp/base/Function.h" 00025 #include "zypp/base/Regex.h" 00026 #include "zypp/PathInfo.h" 00027 #include "zypp/TmpPath.h" 00028 00029 #include "zypp/ServiceInfo.h" 00030 #include "zypp/repo/RepoException.h" 00031 #include "zypp/RepoManager.h" 00032 00033 #include "zypp/media/MediaManager.h" 00034 #include "zypp/media/CredentialManager.h" 00035 #include "zypp/MediaSetAccess.h" 00036 #include "zypp/ExternalProgram.h" 00037 #include "zypp/ManagedFile.h" 00038 00039 #include "zypp/parser/RepoFileReader.h" 00040 #include "zypp/parser/ServiceFileReader.h" 00041 #include "zypp/repo/ServiceRepos.h" 00042 #include "zypp/repo/yum/Downloader.h" 00043 #include "zypp/repo/susetags/Downloader.h" 00044 #include "zypp/parser/plaindir/RepoParser.h" 00045 #include "zypp/repo/PluginServices.h" 00046 00047 #include "zypp/Target.h" // for Target::targetDistribution() for repo index services 00048 #include "zypp/ZYppFactory.h" // to get the Target from ZYpp instance 00049 #include "zypp/HistoryLog.h" // to write history :O) 00050 00051 #include "zypp/ZYppCallbacks.h" 00052 00053 #include "sat/Pool.h" 00054 00055 using std::endl; 00056 using std::string; 00057 using namespace zypp::repo; 00058 00060 namespace zypp 00061 { 00062 00063 namespace 00064 { 00086 class UrlCredentialExtractor 00087 { 00088 public: 00089 UrlCredentialExtractor( Pathname & root_r ) 00090 : _root( root_r ) 00091 {} 00092 00093 ~UrlCredentialExtractor() 00094 { if ( _cmPtr ) _cmPtr->save(); } 00095 00097 bool collect( const Url & url_r ) 00098 { 00099 bool ret = url_r.hasCredentialsInAuthority(); 00100 if ( ret ) 00101 { 00102 if ( !_cmPtr ) _cmPtr.reset( new media::CredentialManager( _root ) ); 00103 _cmPtr->addUserCred( url_r ); 00104 } 00105 return ret; 00106 } 00108 template<class TContainer> 00109 bool collect( const TContainer & urls_r ) 00110 { bool ret = false; for_( it, urls_r.begin(), urls_r.end() ) { const Url & url( *it ); if ( collect( url ) && !ret ) ret = true; } return ret; } 00111 00113 bool extract( Url & url_r ) 00114 { 00115 bool ret = collect( url_r ); 00116 if ( ret ) 00117 url_r.setPassword( std::string() ); 00118 return ret; 00119 } 00121 template<class TContainer> 00122 bool extract( TContainer & urls_r ) 00123 { bool ret = false; for_( it, urls_r.begin(), urls_r.end() ) { const Url & url( *it ); if ( extract( url ) && !ret ) ret = true; } return ret; } 00124 00125 private: 00126 const Pathname & _root; 00127 scoped_ptr<media::CredentialManager> _cmPtr; 00128 }; 00129 } // namespace 00131 00133 namespace 00134 { 00138 class MediaMounter 00139 { 00140 public: 00142 MediaMounter( const Url & url_r ) 00143 { 00144 media::MediaManager mediamanager; 00145 _mid = mediamanager.open( url_r ); 00146 mediamanager.attach( _mid ); 00147 } 00148 00150 ~MediaMounter() 00151 { 00152 media::MediaManager mediamanager; 00153 mediamanager.release( _mid ); 00154 mediamanager.close( _mid ); 00155 } 00156 00161 Pathname getPathName( const Pathname & path_r = Pathname() ) const 00162 { 00163 media::MediaManager mediamanager; 00164 return mediamanager.localPath( _mid, path_r ); 00165 } 00166 00167 private: 00168 media::MediaAccessId _mid; 00169 }; 00170 00172 template <class Iterator> 00173 inline bool foundAliasIn( const std::string & alias_r, Iterator begin_r, Iterator end_r ) 00174 { 00175 for_( it, begin_r, end_r ) 00176 if ( it->alias() == alias_r ) 00177 return true; 00178 return false; 00179 } 00181 template <class Container> 00182 inline bool foundAliasIn( const std::string & alias_r, const Container & cont_r ) 00183 { return foundAliasIn( alias_r, cont_r.begin(), cont_r.end() ); } 00184 00186 template <class Iterator> 00187 inline Iterator findAlias( const std::string & alias_r, Iterator begin_r, Iterator end_r ) 00188 { 00189 for_( it, begin_r, end_r ) 00190 if ( it->alias() == alias_r ) 00191 return it; 00192 return end_r; 00193 } 00195 template <class Container> 00196 inline typename Container::iterator findAlias( const std::string & alias_r, Container & cont_r ) 00197 { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); } 00199 template <class Container> 00200 inline typename Container::const_iterator findAlias( const std::string & alias_r, const Container & cont_r ) 00201 { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); } 00202 } 00203 00205 // 00206 // CLASS NAME : RepoManagerOptions 00207 // 00209 00210 RepoManagerOptions::RepoManagerOptions( const Pathname & root_r ) 00211 { 00212 repoCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoCachePath() ); 00213 repoRawCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoMetadataPath() ); 00214 repoSolvCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoSolvfilesPath() ); 00215 repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() ); 00216 knownReposPath = Pathname::assertprefix( root_r, ZConfig::instance().knownReposPath() ); 00217 knownServicesPath = Pathname::assertprefix( root_r, ZConfig::instance().knownServicesPath() ); 00218 pluginsPath = Pathname::assertprefix( root_r, ZConfig::instance().pluginsPath() ); 00219 probe = ZConfig::instance().repo_add_probe(); 00220 00221 rootDir = root_r; 00222 } 00223 00224 RepoManagerOptions RepoManagerOptions::makeTestSetup( const Pathname & root_r ) 00225 { 00226 RepoManagerOptions ret; 00227 ret.repoCachePath = root_r; 00228 ret.repoRawCachePath = root_r/"raw"; 00229 ret.repoSolvCachePath = root_r/"solv"; 00230 ret.repoPackagesCachePath = root_r/"packages"; 00231 ret.knownReposPath = root_r/"repos.d"; 00232 ret.knownServicesPath = root_r/"services.d"; 00233 ret.pluginsPath = root_r/"plugins"; 00234 ret.rootDir = root_r; 00235 return ret; 00236 } 00237 00239 00255 struct RepoCollector : private base::NonCopyable 00256 { 00257 RepoCollector() 00258 {} 00259 00260 RepoCollector(const std::string & targetDistro_) 00261 : targetDistro(targetDistro_) 00262 {} 00263 00264 bool collect( const RepoInfo &repo ) 00265 { 00266 // skip repositories meant for other distros than specified 00267 if (!targetDistro.empty() 00268 && !repo.targetDistribution().empty() 00269 && repo.targetDistribution() != targetDistro) 00270 { 00271 MIL 00272 << "Skipping repository meant for '" << targetDistro 00273 << "' distribution (current distro is '" 00274 << repo.targetDistribution() << "')." << endl; 00275 00276 return true; 00277 } 00278 00279 repos.push_back(repo); 00280 return true; 00281 } 00282 00283 RepoInfoList repos; 00284 std::string targetDistro; 00285 }; 00286 00288 00294 static std::list<RepoInfo> repositories_in_file( const Pathname & file ) 00295 { 00296 MIL << "repo file: " << file << endl; 00297 RepoCollector collector; 00298 parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) ); 00299 return collector.repos; 00300 } 00301 00303 00312 static std::list<RepoInfo> repositories_in_dir( const Pathname &dir ) 00313 { 00314 MIL << "directory " << dir << endl; 00315 std::list<RepoInfo> repos; 00316 std::list<Pathname> entries; 00317 if ( filesystem::readdir( entries, dir, false ) != 0 ) 00318 { 00319 // TranslatorExplanation '%s' is a pathname 00320 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str()))); 00321 } 00322 00323 str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$"); 00324 for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it ) 00325 { 00326 if (str::regex_match(it->extension(), allowedRepoExt)) 00327 { 00328 std::list<RepoInfo> tmp = repositories_in_file( *it ); 00329 repos.insert( repos.end(), tmp.begin(), tmp.end() ); 00330 00331 //std::copy( collector.repos.begin(), collector.repos.end(), std::back_inserter(repos)); 00332 //MIL << "ok" << endl; 00333 } 00334 } 00335 return repos; 00336 } 00337 00339 00340 std::list<RepoInfo> readRepoFile(const Url & repo_file) 00341 { 00342 // no interface to download a specific file, using workaround: 00344 Url url(repo_file); 00345 Pathname path(url.getPathName()); 00346 url.setPathName ("/"); 00347 MediaSetAccess access(url); 00348 Pathname local = access.provideFile(path); 00349 00350 DBG << "reading repo file " << repo_file << ", local path: " << local << endl; 00351 00352 return repositories_in_file(local); 00353 } 00354 00356 00357 inline void assert_alias( const RepoInfo & info ) 00358 { 00359 if ( info.alias().empty() ) 00360 ZYPP_THROW( RepoNoAliasException() ); 00361 // bnc #473834. Maybe we can match the alias against a regex to define 00362 // and check for valid aliases 00363 if ( info.alias()[0] == '.') 00364 ZYPP_THROW(RepoInvalidAliasException( 00365 info, _("Repository alias cannot start with dot."))); 00366 } 00367 00368 inline void assert_alias( const ServiceInfo & info ) 00369 { 00370 if ( info.alias().empty() ) 00371 ZYPP_THROW( ServiceNoAliasException() ); 00372 // bnc #473834. Maybe we can match the alias against a regex to define 00373 // and check for valid aliases 00374 if ( info.alias()[0] == '.') 00375 ZYPP_THROW(ServiceInvalidAliasException( 00376 info, _("Service alias cannot start with dot."))); 00377 } 00378 00380 00381 inline void assert_urls( const RepoInfo & info ) 00382 { 00383 if ( info.baseUrlsEmpty() ) 00384 ZYPP_THROW( RepoNoUrlException( info ) ); 00385 } 00386 00387 inline void assert_url( const ServiceInfo & info ) 00388 { 00389 if ( ! info.url().isValid() ) 00390 ZYPP_THROW( ServiceNoUrlException( info ) ); 00391 } 00392 00394 00399 inline Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info ) 00400 { 00401 assert_alias(info); 00402 return opt.repoRawCachePath / info.escaped_alias(); 00403 } 00404 00413 inline Pathname rawproductdata_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info ) 00414 { 00415 assert_alias(info); 00416 return opt.repoRawCachePath / info.escaped_alias() / info.path(); 00417 } 00418 00419 00423 inline Pathname packagescache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info ) 00424 { 00425 assert_alias(info); 00426 return opt.repoPackagesCachePath / info.escaped_alias(); 00427 } 00428 00432 inline Pathname solv_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info) 00433 { 00434 assert_alias(info); 00435 return opt.repoSolvCachePath / info.escaped_alias(); 00436 } 00437 00439 00441 class ServiceCollector 00442 { 00443 public: 00444 typedef std::set<ServiceInfo> ServiceSet; 00445 00446 ServiceCollector( ServiceSet & services_r ) 00447 : _services( services_r ) 00448 {} 00449 00450 bool operator()( const ServiceInfo & service_r ) const 00451 { 00452 _services.insert( service_r ); 00453 return true; 00454 } 00455 00456 private: 00457 ServiceSet & _services; 00458 }; 00459 00461 00463 // 00464 // CLASS NAME : RepoManager::Impl 00465 // 00467 00471 struct RepoManager::Impl 00472 { 00473 Impl( const RepoManagerOptions &opt ) 00474 : options(opt) 00475 { 00476 init_knownServices(); 00477 init_knownRepositories(); 00478 } 00479 00480 00481 RepoManagerOptions options; 00482 00483 RepoSet repos; 00484 00485 ServiceSet services; 00486 00487 public: 00488 00489 void saveService( ServiceInfo & service ) const; 00490 00491 Pathname generateNonExistingName( const Pathname &dir, 00492 const std::string &basefilename ) const; 00493 00494 std::string generateFilename( const RepoInfo & info ) const; 00495 std::string generateFilename( const ServiceInfo & info ) const; 00496 00497 00498 private: 00499 void init_knownServices(); 00500 void init_knownRepositories(); 00501 00502 private: 00503 friend Impl * rwcowClone<Impl>( const Impl * rhs ); 00505 Impl * clone() const 00506 { return new Impl( *this ); } 00507 }; 00508 00510 00512 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj ) 00513 { 00514 return str << "RepoManager::Impl"; 00515 } 00516 00518 00519 void RepoManager::Impl::saveService( ServiceInfo & service ) const 00520 { 00521 filesystem::assert_dir( options.knownServicesPath ); 00522 Pathname servfile = generateNonExistingName( options.knownServicesPath, 00523 generateFilename( service ) ); 00524 service.setFilepath( servfile ); 00525 00526 MIL << "saving service in " << servfile << endl; 00527 00528 std::ofstream file( servfile.c_str() ); 00529 if ( !file ) 00530 { 00531 // TranslatorExplanation '%s' is a filename 00532 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), servfile.c_str() ))); 00533 } 00534 service.dumpAsIniOn( file ); 00535 MIL << "done" << endl; 00536 } 00537 00553 Pathname RepoManager::Impl::generateNonExistingName( const Pathname & dir, 00554 const std::string & basefilename ) const 00555 { 00556 std::string final_filename = basefilename; 00557 int counter = 1; 00558 while ( PathInfo(dir + final_filename).isExist() ) 00559 { 00560 final_filename = basefilename + "_" + str::numstring(counter); 00561 counter++; 00562 } 00563 return dir + Pathname(final_filename); 00564 } 00565 00567 00575 std::string RepoManager::Impl::generateFilename( const RepoInfo & info ) const 00576 { 00577 std::string filename = info.alias(); 00578 // replace slashes with underscores 00579 str::replaceAll( filename, "/", "_" ); 00580 00581 filename = Pathname(filename).extend(".repo").asString(); 00582 MIL << "generating filename for repo [" << info.alias() << "] : '" << filename << "'" << endl; 00583 return filename; 00584 } 00585 00586 std::string RepoManager::Impl::generateFilename( const ServiceInfo & info ) const 00587 { 00588 std::string filename = info.alias(); 00589 // replace slashes with underscores 00590 str::replaceAll( filename, "/", "_" ); 00591 00592 filename = Pathname(filename).extend(".service").asString(); 00593 MIL << "generating filename for service [" << info.alias() << "] : '" << filename << "'" << endl; 00594 return filename; 00595 } 00596 00597 00598 void RepoManager::Impl::init_knownServices() 00599 { 00600 Pathname dir = options.knownServicesPath; 00601 std::list<Pathname> entries; 00602 if (PathInfo(dir).isExist()) 00603 { 00604 if ( filesystem::readdir( entries, dir, false ) != 0 ) 00605 { 00606 // TranslatorExplanation '%s' is a pathname 00607 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str()))); 00608 } 00609 00610 //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$"); 00611 for_(it, entries.begin(), entries.end() ) 00612 { 00613 parser::ServiceFileReader(*it, ServiceCollector(services)); 00614 } 00615 } 00616 00617 repo::PluginServices(options.pluginsPath/"services", ServiceCollector(services)); 00618 } 00619 00620 void RepoManager::Impl::init_knownRepositories() 00621 { 00622 MIL << "start construct known repos" << endl; 00623 00624 if ( PathInfo(options.knownReposPath).isExist() ) 00625 { 00626 RepoInfoList repol = repositories_in_dir(options.knownReposPath); 00627 std::list<string> repo_esc_aliases; 00628 std::list<string> entries; 00629 for ( RepoInfoList::iterator it = repol.begin(); 00630 it != repol.end(); 00631 ++it ) 00632 { 00633 // set the metadata path for the repo 00634 Pathname metadata_path = rawcache_path_for_repoinfo(options, (*it)); 00635 (*it).setMetadataPath(metadata_path); 00636 00637 // set the downloaded packages path for the repo 00638 Pathname packages_path = packagescache_path_for_repoinfo(options, (*it)); 00639 (*it).setPackagesPath(packages_path); 00640 00641 repos.insert(*it); 00642 repo_esc_aliases.push_back(it->escaped_alias()); 00643 } 00644 00645 // delete metadata folders without corresponding repo (e.g. old tmp directories) 00646 if ( filesystem::readdir( entries, options.repoRawCachePath, false ) == 0 ) 00647 { 00648 std::set<string> oldfiles; 00649 repo_esc_aliases.sort(); 00650 entries.sort(); 00651 set_difference(entries.begin(), entries.end(), repo_esc_aliases.begin(), repo_esc_aliases.end(), std::inserter(oldfiles, oldfiles.end())); 00652 for_(it, oldfiles.begin(), oldfiles.end()) 00653 { 00654 filesystem::recursive_rmdir(options.repoRawCachePath / *it); 00655 } 00656 } 00657 } 00658 00659 MIL << "end construct known repos" << endl; 00660 } 00661 00663 // 00664 // CLASS NAME : RepoManager 00665 // 00667 00668 RepoManager::RepoManager( const RepoManagerOptions &opt ) 00669 : _pimpl( new Impl(opt) ) 00670 {} 00671 00673 00674 RepoManager::~RepoManager() 00675 {} 00676 00678 00679 bool RepoManager::repoEmpty() const 00680 { return _pimpl->repos.empty(); } 00681 00682 RepoManager::RepoSizeType RepoManager::repoSize() const 00683 { return _pimpl->repos.size(); } 00684 00685 RepoManager::RepoConstIterator RepoManager::repoBegin() const 00686 { return _pimpl->repos.begin(); } 00687 00688 RepoManager::RepoConstIterator RepoManager::repoEnd() const 00689 { return _pimpl->repos.end(); } 00690 00691 RepoInfo RepoManager::getRepo( const std::string & alias ) const 00692 { 00693 for_( it, repoBegin(), repoEnd() ) 00694 if ( it->alias() == alias ) 00695 return *it; 00696 return RepoInfo::noRepo; 00697 } 00698 00699 bool RepoManager::hasRepo( const std::string & alias ) const 00700 { 00701 for_( it, repoBegin(), repoEnd() ) 00702 if ( it->alias() == alias ) 00703 return true; 00704 return false; 00705 } 00706 00707 std::string RepoManager::makeStupidAlias( const Url & url_r ) 00708 { 00709 std::string ret( url_r.getScheme() ); 00710 if ( ret.empty() ) 00711 ret = "repo-"; 00712 else 00713 ret += "-"; 00714 00715 std::string host( url_r.getHost() ); 00716 if ( ! host.empty() ) 00717 { 00718 ret += host; 00719 ret += "-"; 00720 } 00721 00722 static Date::ValueType serial = Date::now(); 00723 ret += Digest::digest( Digest::sha1(), str::hexstring( ++serial ) +url_r.asCompleteString() ).substr(0,8); 00724 return ret; 00725 } 00726 00728 00729 Pathname RepoManager::metadataPath( const RepoInfo &info ) const 00730 { 00731 return rawcache_path_for_repoinfo(_pimpl->options, info ); 00732 } 00733 00734 Pathname RepoManager::packagesPath( const RepoInfo &info ) const 00735 { 00736 return packagescache_path_for_repoinfo(_pimpl->options, info ); 00737 } 00738 00740 00741 RepoStatus RepoManager::metadataStatus( const RepoInfo &info ) const 00742 { 00743 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info ); 00744 Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info ); 00745 RepoType repokind = info.type(); 00746 RepoStatus status; 00747 00748 switch ( repokind.toEnum() ) 00749 { 00750 case RepoType::NONE_e: 00751 // unknown, probe the local metadata 00752 repokind = probe( productdatapath.asUrl() ); 00753 break; 00754 default: 00755 break; 00756 } 00757 00758 switch ( repokind.toEnum() ) 00759 { 00760 case RepoType::RPMMD_e : 00761 { 00762 status = RepoStatus( productdatapath + "/repodata/repomd.xml"); 00763 } 00764 break; 00765 00766 case RepoType::YAST2_e : 00767 { 00768 status = RepoStatus( productdatapath + "/content") && (RepoStatus( mediarootpath + "/media.1/media")); 00769 } 00770 break; 00771 00772 case RepoType::RPMPLAINDIR_e : 00773 { 00774 if ( PathInfo(Pathname(productdatapath + "/cookie")).isExist() ) 00775 status = RepoStatus( productdatapath + "/cookie"); 00776 } 00777 break; 00778 00779 case RepoType::NONE_e : 00780 // Return default RepoStatus in case of RepoType::NONE 00781 // indicating it should be created? 00782 // ZYPP_THROW(RepoUnknownTypeException()); 00783 break; 00784 } 00785 return status; 00786 } 00787 00788 void RepoManager::touchIndexFile(const RepoInfo & info) 00789 { 00790 Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info ); 00791 00792 RepoType repokind = info.type(); 00793 if ( repokind.toEnum() == RepoType::NONE_e ) 00794 // unknown, probe the local metadata 00795 repokind = probe( productdatapath.asUrl() ); 00796 // if still unknown, just return 00797 if (repokind == RepoType::NONE_e) 00798 return; 00799 00800 Pathname p; 00801 switch ( repokind.toEnum() ) 00802 { 00803 case RepoType::RPMMD_e : 00804 p = Pathname(productdatapath + "/repodata/repomd.xml"); 00805 break; 00806 00807 case RepoType::YAST2_e : 00808 p = Pathname(productdatapath + "/content"); 00809 break; 00810 00811 case RepoType::RPMPLAINDIR_e : 00812 p = Pathname(productdatapath + "/cookie"); 00813 break; 00814 00815 case RepoType::NONE_e : 00816 default: 00817 break; 00818 } 00819 00820 // touch the file, ignore error (they are logged anyway) 00821 filesystem::touch(p); 00822 } 00823 00824 RepoManager::RefreshCheckStatus RepoManager::checkIfToRefreshMetadata( 00825 const RepoInfo &info, 00826 const Url &url, 00827 RawMetadataRefreshPolicy policy ) 00828 { 00829 assert_alias(info); 00830 00831 RepoStatus oldstatus; 00832 RepoStatus newstatus; 00833 00834 try 00835 { 00836 MIL << "Going to try to check whether refresh is needed for " << url << endl; 00837 00838 // first check old (cached) metadata 00839 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info ); 00840 filesystem::assert_dir(mediarootpath); 00841 oldstatus = metadataStatus(info); 00842 00843 if ( oldstatus.empty() ) 00844 { 00845 MIL << "No cached metadata, going to refresh" << endl; 00846 return REFRESH_NEEDED; 00847 } 00848 00849 { 00850 std::string scheme( url.getScheme() ); 00851 if ( scheme == "cd" || scheme == "dvd" ) 00852 { 00853 MIL << "never refresh CD/DVD" << endl; 00854 return REPO_UP_TO_DATE; 00855 } 00856 } 00857 00858 // now we've got the old (cached) status, we can decide repo.refresh.delay 00859 if (policy != RefreshForced && policy != RefreshIfNeededIgnoreDelay) 00860 { 00861 // difference in seconds 00862 double diff = difftime( 00863 (Date::ValueType)Date::now(), 00864 (Date::ValueType)oldstatus.timestamp()) / 60; 00865 00866 DBG << "oldstatus: " << (Date::ValueType)oldstatus.timestamp() << endl; 00867 DBG << "current time: " << (Date::ValueType)Date::now() << endl; 00868 DBG << "last refresh = " << diff << " minutes ago" << endl; 00869 00870 if ( diff < ZConfig::instance().repo_refresh_delay() ) 00871 { 00872 if ( diff < 0 ) 00873 { 00874 WAR << "Repository '" << info.alias() << "' was refreshed in the future!" << endl; 00875 } 00876 else 00877 { 00878 MIL << "Repository '" << info.alias() 00879 << "' has been refreshed less than repo.refresh.delay (" 00880 << ZConfig::instance().repo_refresh_delay() 00881 << ") minutes ago. Advising to skip refresh" << endl; 00882 return REPO_CHECK_DELAYED; 00883 } 00884 } 00885 } 00886 00887 // To test the new matadta create temp dir as sibling of mediarootpath 00888 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) ); 00889 00890 repo::RepoType repokind = info.type(); 00891 // if the type is unknown, try probing. 00892 switch ( repokind.toEnum() ) 00893 { 00894 case RepoType::NONE_e: 00895 // unknown, probe it \todo respect productdir 00896 repokind = probe( url, info.path() ); 00897 break; 00898 default: 00899 break; 00900 } 00901 00902 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) || 00903 ( repokind.toEnum() == RepoType::YAST2_e ) ) 00904 { 00905 MediaSetAccess media(url); 00906 shared_ptr<repo::Downloader> downloader_ptr; 00907 00908 if ( repokind.toEnum() == RepoType::RPMMD_e ) 00909 downloader_ptr.reset(new yum::Downloader(info, mediarootpath)); 00910 else 00911 downloader_ptr.reset( new susetags::Downloader(info, mediarootpath)); 00912 00913 RepoStatus newstatus = downloader_ptr->status(media); 00914 bool refresh = false; 00915 if ( oldstatus.checksum() == newstatus.checksum() ) 00916 { 00917 MIL << "repo has not changed" << endl; 00918 if ( policy == RefreshForced ) 00919 { 00920 MIL << "refresh set to forced" << endl; 00921 refresh = true; 00922 } 00923 } 00924 else 00925 { 00926 MIL << "repo has changed, going to refresh" << endl; 00927 refresh = true; 00928 } 00929 00930 if (!refresh) 00931 touchIndexFile(info); 00932 00933 return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE; 00934 } 00935 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e ) 00936 { 00937 MediaMounter media( url ); 00938 RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) ); 00939 bool refresh = false; 00940 if ( oldstatus.checksum() == newstatus.checksum() ) 00941 { 00942 MIL << "repo has not changed" << endl; 00943 if ( policy == RefreshForced ) 00944 { 00945 MIL << "refresh set to forced" << endl; 00946 refresh = true; 00947 } 00948 } 00949 else 00950 { 00951 MIL << "repo has changed, going to refresh" << endl; 00952 refresh = true; 00953 } 00954 00955 if (!refresh) 00956 touchIndexFile(info); 00957 00958 return refresh ? REFRESH_NEEDED : REPO_UP_TO_DATE; 00959 } 00960 else 00961 { 00962 ZYPP_THROW(RepoUnknownTypeException(info)); 00963 } 00964 } 00965 catch ( const Exception &e ) 00966 { 00967 ZYPP_CAUGHT(e); 00968 ERR << "refresh check failed for " << url << endl; 00969 ZYPP_RETHROW(e); 00970 } 00971 00972 return REFRESH_NEEDED; // default 00973 } 00974 00975 void RepoManager::refreshMetadata( const RepoInfo &info, 00976 RawMetadataRefreshPolicy policy, 00977 const ProgressData::ReceiverFnc & progress ) 00978 { 00979 assert_alias(info); 00980 assert_urls(info); 00981 00982 // we will throw this later if no URL checks out fine 00983 RepoException rexception(_("Valid metadata not found at specified URL(s)")); 00984 00985 // Suppress (interactive) media::MediaChangeReport if we in have multiple basurls (>1) 00986 media::ScopedDisableMediaChangeReport guard( info.baseUrlsSize() > 1 ); 00987 00988 // try urls one by one 00989 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it ) 00990 { 00991 try 00992 { 00993 Url url(*it); 00994 00995 // check whether to refresh metadata 00996 // if the check fails for this url, it throws, so another url will be checked 00997 if (checkIfToRefreshMetadata(info, url, policy)!=REFRESH_NEEDED) 00998 return; 00999 01000 MIL << "Going to refresh metadata from " << url << endl; 01001 01002 repo::RepoType repokind = info.type(); 01003 01004 // if the type is unknown, try probing. 01005 switch ( repokind.toEnum() ) 01006 { 01007 case RepoType::NONE_e: 01008 // unknown, probe it 01009 repokind = probe( *it, info.path() ); 01010 01011 if (repokind.toEnum() != RepoType::NONE_e) 01012 { 01013 // Adjust the probed type in RepoInfo 01014 info.setProbedType( repokind ); // lazy init! 01015 //save probed type only for repos in system 01016 for_( it, repoBegin(), repoEnd() ) 01017 { 01018 if ( info.alias() == (*it).alias() ) 01019 { 01020 RepoInfo modifiedrepo = info; 01021 modifiedrepo.setType( repokind ); 01022 modifyRepository( info.alias(), modifiedrepo ); 01023 break; 01024 } 01025 } 01026 } 01027 break; 01028 default: 01029 break; 01030 } 01031 01032 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info ); 01033 if( filesystem::assert_dir(mediarootpath) ) 01034 { 01035 Exception ex(str::form( _("Can't create %s"), mediarootpath.c_str()) ); 01036 ZYPP_THROW(ex); 01037 } 01038 01039 // create temp dir as sibling of mediarootpath 01040 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) ); 01041 if( tmpdir.path().empty() ) 01042 { 01043 Exception ex(_("Can't create metadata cache directory.")); 01044 ZYPP_THROW(ex); 01045 } 01046 01047 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) || 01048 ( repokind.toEnum() == RepoType::YAST2_e ) ) 01049 { 01050 MediaSetAccess media(url); 01051 shared_ptr<repo::Downloader> downloader_ptr; 01052 01053 MIL << "Creating downloader for [ " << info.alias() << " ]" << endl; 01054 01055 if ( repokind.toEnum() == RepoType::RPMMD_e ) 01056 downloader_ptr.reset(new yum::Downloader(info, mediarootpath)); 01057 else 01058 downloader_ptr.reset( new susetags::Downloader(info, mediarootpath) ); 01059 01066 for_( it, repoBegin(), repoEnd() ) 01067 { 01068 Pathname cachepath(rawcache_path_for_repoinfo( _pimpl->options, *it )); 01069 if ( PathInfo(cachepath).isExist() ) 01070 downloader_ptr->addCachePath(cachepath); 01071 } 01072 01073 downloader_ptr->download( media, tmpdir.path() ); 01074 } 01075 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e ) 01076 { 01077 MediaMounter media( url ); 01078 RepoStatus newstatus = parser::plaindir::dirStatus( media.getPathName( info.path() ) ); 01079 01080 Pathname productpath( tmpdir.path() / info.path() ); 01081 filesystem::assert_dir( productpath ); 01082 std::ofstream file( (productpath/"cookie").c_str() ); 01083 if ( !file ) 01084 { 01085 // TranslatorExplanation '%s' is a filename 01086 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), (productpath/"cookie").c_str() ))); 01087 } 01088 file << url; 01089 if ( ! info.path().empty() && info.path() != "/" ) 01090 file << " (" << info.path() << ")"; 01091 file << endl; 01092 file << newstatus.checksum() << endl; 01093 01094 file.close(); 01095 } 01096 else 01097 { 01098 ZYPP_THROW(RepoUnknownTypeException()); 01099 } 01100 01101 // ok we have the metadata, now exchange 01102 // the contents 01103 filesystem::exchange( tmpdir.path(), mediarootpath ); 01104 01105 // we are done. 01106 return; 01107 } 01108 catch ( const Exception &e ) 01109 { 01110 ZYPP_CAUGHT(e); 01111 ERR << "Trying another url..." << endl; 01112 01113 // remember the exception caught for the *first URL* 01114 // if all other URLs fail, the rexception will be thrown with the 01115 // cause of the problem of the first URL remembered 01116 if (it == info.baseUrlsBegin()) 01117 rexception.remember(e); 01118 else 01119 rexception.addHistory( e.asUserString() ); 01120 01121 } 01122 } // for every url 01123 ERR << "No more urls..." << endl; 01124 ZYPP_THROW(rexception); 01125 } 01126 01128 01129 void RepoManager::cleanMetadata( const RepoInfo &info, 01130 const ProgressData::ReceiverFnc & progressfnc ) 01131 { 01132 ProgressData progress(100); 01133 progress.sendTo(progressfnc); 01134 01135 filesystem::recursive_rmdir(rawcache_path_for_repoinfo(_pimpl->options, info)); 01136 progress.toMax(); 01137 } 01138 01139 void RepoManager::cleanPackages( const RepoInfo &info, 01140 const ProgressData::ReceiverFnc & progressfnc ) 01141 { 01142 ProgressData progress(100); 01143 progress.sendTo(progressfnc); 01144 01145 filesystem::recursive_rmdir(packagescache_path_for_repoinfo(_pimpl->options, info)); 01146 progress.toMax(); 01147 } 01148 01149 void RepoManager::buildCache( const RepoInfo &info, 01150 CacheBuildPolicy policy, 01151 const ProgressData::ReceiverFnc & progressrcv ) 01152 { 01153 assert_alias(info); 01154 Pathname mediarootpath = rawcache_path_for_repoinfo( _pimpl->options, info ); 01155 Pathname productdatapath = rawproductdata_path_for_repoinfo( _pimpl->options, info ); 01156 01157 if( filesystem::assert_dir(_pimpl->options.repoCachePath) ) 01158 { 01159 Exception ex(str::form( _("Can't create %s"), _pimpl->options.repoCachePath.c_str()) ); 01160 ZYPP_THROW(ex); 01161 } 01162 RepoStatus raw_metadata_status = metadataStatus(info); 01163 if ( raw_metadata_status.empty() ) 01164 { 01165 /* if there is no cache at this point, we refresh the raw 01166 in case this is the first time - if it's !autorefresh, 01167 we may still refresh */ 01168 refreshMetadata(info, RefreshIfNeeded, progressrcv ); 01169 raw_metadata_status = metadataStatus(info); 01170 } 01171 01172 bool needs_cleaning = false; 01173 if ( isCached( info ) ) 01174 { 01175 MIL << info.alias() << " is already cached." << endl; 01176 RepoStatus cache_status = cacheStatus(info); 01177 01178 if ( cache_status.checksum() == raw_metadata_status.checksum() ) 01179 { 01180 MIL << info.alias() << " cache is up to date with metadata." << endl; 01181 if ( policy == BuildIfNeeded ) { 01182 return; 01183 } 01184 else { 01185 MIL << info.alias() << " cache rebuild is forced" << endl; 01186 } 01187 } 01188 01189 needs_cleaning = true; 01190 } 01191 01192 ProgressData progress(100); 01193 callback::SendReport<ProgressReport> report; 01194 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) ); 01195 progress.name(str::form(_("Building repository '%s' cache"), info.label().c_str())); 01196 progress.toMin(); 01197 01198 if (needs_cleaning) 01199 { 01200 cleanCache(info); 01201 } 01202 01203 MIL << info.alias() << " building cache..." << info.type() << endl; 01204 01205 Pathname base = solv_path_for_repoinfo( _pimpl->options, info); 01206 01207 if( filesystem::assert_dir(base) ) 01208 { 01209 Exception ex(str::form( _("Can't create %s"), base.c_str()) ); 01210 ZYPP_THROW(ex); 01211 } 01212 01213 if( ! PathInfo(base).userMayW() ) 01214 { 01215 Exception ex(str::form( _("Can't create cache at %s - no writing permissions."), base.c_str()) ); 01216 ZYPP_THROW(ex); 01217 } 01218 Pathname solvfile = base / "solv"; 01219 01220 // do we have type? 01221 repo::RepoType repokind = info.type(); 01222 01223 // if the type is unknown, try probing. 01224 switch ( repokind.toEnum() ) 01225 { 01226 case RepoType::NONE_e: 01227 // unknown, probe the local metadata 01228 repokind = probe( productdatapath.asUrl() ); 01229 break; 01230 default: 01231 break; 01232 } 01233 01234 MIL << "repo type is " << repokind << endl; 01235 01236 switch ( repokind.toEnum() ) 01237 { 01238 case RepoType::RPMMD_e : 01239 case RepoType::YAST2_e : 01240 case RepoType::RPMPLAINDIR_e : 01241 { 01242 // Take care we unlink the solvfile on exception 01243 ManagedFile guard( solvfile, filesystem::unlink ); 01244 scoped_ptr<MediaMounter> forPlainDirs; 01245 01246 ExternalProgram::Arguments cmd; 01247 cmd.push_back( "repo2solv.sh" ); 01248 01249 // repo2solv expects -o as 1st arg! 01250 cmd.push_back( "-o" ); 01251 cmd.push_back( solvfile.asString() ); 01252 01253 if ( repokind == RepoType::RPMPLAINDIR ) 01254 { 01255 forPlainDirs.reset( new MediaMounter( *info.baseUrlsBegin() ) ); 01256 // recusive for plaindir as 2nd arg! 01257 cmd.push_back( "-R" ); 01258 // FIXME this does only work form dir: URLs 01259 cmd.push_back( forPlainDirs->getPathName( info.path() ).c_str() ); 01260 } 01261 else 01262 cmd.push_back( productdatapath.asString() ); 01263 01264 ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout ); 01265 std::string errdetail; 01266 01267 for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) { 01268 WAR << " " << output; 01269 if ( errdetail.empty() ) { 01270 errdetail = prog.command(); 01271 errdetail += '\n'; 01272 } 01273 errdetail += output; 01274 } 01275 01276 int ret = prog.close(); 01277 if ( ret != 0 ) 01278 { 01279 RepoException ex(str::form( _("Failed to cache repo (%d)."), ret )); 01280 ex.remember( errdetail ); 01281 ZYPP_THROW(ex); 01282 } 01283 01284 // We keep it. 01285 guard.resetDispose(); 01286 } 01287 break; 01288 default: 01289 ZYPP_THROW(RepoUnknownTypeException( _("Unhandled repository type") )); 01290 break; 01291 } 01292 // update timestamp and checksum 01293 setCacheStatus(info, raw_metadata_status); 01294 MIL << "Commit cache.." << endl; 01295 progress.toMax(); 01296 } 01297 01299 01300 repo::RepoType RepoManager::probe( const Url & url ) const 01301 { return probe( url, Pathname() ); } 01302 01303 repo::RepoType RepoManager::probe( const Url & url, const Pathname & path ) const 01304 { 01305 MIL << "going to probe the repo type at " << url << " (" << path << ")" << endl; 01306 01307 if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName()/path ).isDir() ) 01308 { 01309 // Handle non existing local directory in advance, as 01310 // MediaSetAccess does not support it. 01311 MIL << "Probed type NONE (not exists) at " << url << " (" << path << ")" << endl; 01312 return repo::RepoType::NONE; 01313 } 01314 01315 // prepare exception to be thrown if the type could not be determined 01316 // due to a media exception. We can't throw right away, because of some 01317 // problems with proxy servers returning an incorrect error 01318 // on ftp file-not-found(bnc #335906). Instead we'll check another types 01319 // before throwing. 01320 01321 // TranslatorExplanation '%s' is an URL 01322 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() )); 01323 bool gotMediaException = false; 01324 try 01325 { 01326 MediaSetAccess access(url); 01327 try 01328 { 01329 if ( access.doesFileExist(path/"/repodata/repomd.xml") ) 01330 { 01331 MIL << "Probed type RPMMD at " << url << " (" << path << ")" << endl; 01332 return repo::RepoType::RPMMD; 01333 } 01334 } 01335 catch ( const media::MediaException &e ) 01336 { 01337 ZYPP_CAUGHT(e); 01338 DBG << "problem checking for repodata/repomd.xml file" << endl; 01339 enew.remember(e); 01340 gotMediaException = true; 01341 } 01342 01343 try 01344 { 01345 if ( access.doesFileExist(path/"/content") ) 01346 { 01347 MIL << "Probed type YAST2 at " << url << " (" << path << ")" << endl; 01348 return repo::RepoType::YAST2; 01349 } 01350 } 01351 catch ( const media::MediaException &e ) 01352 { 01353 ZYPP_CAUGHT(e); 01354 DBG << "problem checking for content file" << endl; 01355 enew.remember(e); 01356 gotMediaException = true; 01357 } 01358 01359 // if it is a non-downloading URL denoting a directory 01360 if ( ! url.schemeIsDownloading() ) 01361 { 01362 MediaMounter media( url ); 01363 if ( PathInfo(media.getPathName()/path).isDir() ) 01364 { 01365 // allow empty dirs for now 01366 MIL << "Probed type RPMPLAINDIR at " << url << " (" << path << ")" << endl; 01367 return repo::RepoType::RPMPLAINDIR; 01368 } 01369 } 01370 } 01371 catch ( const Exception &e ) 01372 { 01373 ZYPP_CAUGHT(e); 01374 // TranslatorExplanation '%s' is an URL 01375 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() )); 01376 enew.remember(e); 01377 ZYPP_THROW(enew); 01378 } 01379 01380 if (gotMediaException) 01381 ZYPP_THROW(enew); 01382 01383 MIL << "Probed type NONE at " << url << " (" << path << ")" << endl; 01384 return repo::RepoType::NONE; 01385 } 01386 01388 01389 void RepoManager::cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv ) 01390 { 01391 MIL << "Going to clean up garbage in cache dirs" << endl; 01392 01393 ProgressData progress(300); 01394 progress.sendTo(progressrcv); 01395 progress.toMin(); 01396 01397 std::list<Pathname> cachedirs; 01398 cachedirs.push_back(_pimpl->options.repoRawCachePath); 01399 cachedirs.push_back(_pimpl->options.repoPackagesCachePath); 01400 cachedirs.push_back(_pimpl->options.repoSolvCachePath); 01401 01402 for_( dir, cachedirs.begin(), cachedirs.end() ) 01403 { 01404 if ( PathInfo(*dir).isExist() ) 01405 { 01406 std::list<Pathname> entries; 01407 if ( filesystem::readdir( entries, *dir, false ) != 0 ) 01408 // TranslatorExplanation '%s' is a pathname 01409 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir->c_str()))); 01410 01411 unsigned sdircount = entries.size(); 01412 unsigned sdircurrent = 1; 01413 for_( subdir, entries.begin(), entries.end() ) 01414 { 01415 // if it does not belong known repo, make it disappear 01416 bool found = false; 01417 for_( r, repoBegin(), repoEnd() ) 01418 if ( subdir->basename() == r->escaped_alias() ) 01419 { found = true; break; } 01420 01421 if ( ! found ) 01422 filesystem::recursive_rmdir( *subdir ); 01423 01424 progress.set( progress.val() + sdircurrent * 100 / sdircount ); 01425 ++sdircurrent; 01426 } 01427 } 01428 else 01429 progress.set( progress.val() + 100 ); 01430 } 01431 progress.toMax(); 01432 } 01433 01435 01436 void RepoManager::cleanCache( const RepoInfo &info, 01437 const ProgressData::ReceiverFnc & progressrcv ) 01438 { 01439 ProgressData progress(100); 01440 progress.sendTo(progressrcv); 01441 progress.toMin(); 01442 01443 MIL << "Removing raw metadata cache for " << info.alias() << endl; 01444 filesystem::recursive_rmdir(solv_path_for_repoinfo(_pimpl->options, info)); 01445 01446 progress.toMax(); 01447 } 01448 01450 01451 bool RepoManager::isCached( const RepoInfo &info ) const 01452 { 01453 return PathInfo(solv_path_for_repoinfo( _pimpl->options, info ) / "solv").isExist(); 01454 } 01455 01456 RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const 01457 { 01458 01459 Pathname cookiefile = solv_path_for_repoinfo(_pimpl->options, info) / "cookie"; 01460 01461 return RepoStatus::fromCookieFile(cookiefile); 01462 } 01463 01464 void RepoManager::setCacheStatus( const RepoInfo &info, const RepoStatus &status ) 01465 { 01466 Pathname base = solv_path_for_repoinfo(_pimpl->options, info); 01467 filesystem::assert_dir(base); 01468 Pathname cookiefile = base / "cookie"; 01469 01470 status.saveToCookieFile(cookiefile); 01471 } 01472 01473 void RepoManager::loadFromCache( const RepoInfo & info, 01474 const ProgressData::ReceiverFnc & progressrcv ) 01475 { 01476 assert_alias(info); 01477 Pathname solvfile = solv_path_for_repoinfo(_pimpl->options, info) / "solv"; 01478 01479 if ( ! PathInfo(solvfile).isExist() ) 01480 ZYPP_THROW(RepoNotCachedException(info)); 01481 01482 try 01483 { 01484 Repository repo = sat::Pool::instance().addRepoSolv( solvfile, info ); 01485 // test toolversion in order to rebuild solv file in case 01486 // it was written by an old satsolver-tool parser. 01487 // 01488 // Known version strings used: 01489 // - <no string> 01490 // - "1.0" 01491 // 01492 sat::LookupRepoAttr toolversion( sat::SolvAttr::repositoryToolVersion, repo ); 01493 if ( toolversion.begin().asString().empty() ) 01494 { 01495 repo.eraseFromPool(); 01496 ZYPP_THROW(Exception("Solv-file was created by old parser.")); 01497 } 01498 // else: up-to-date (or even newer). 01499 } 01500 catch ( const Exception & exp ) 01501 { 01502 ZYPP_CAUGHT( exp ); 01503 MIL << "Try to handle exception by rebuilding the solv-file" << endl; 01504 cleanCache( info, progressrcv ); 01505 buildCache( info, BuildIfNeeded, progressrcv ); 01506 01507 sat::Pool::instance().addRepoSolv( solvfile, info ); 01508 } 01509 } 01510 01512 01513 void RepoManager::addRepository( const RepoInfo &info, 01514 const ProgressData::ReceiverFnc & progressrcv ) 01515 { 01516 assert_alias(info); 01517 01518 ProgressData progress(100); 01519 callback::SendReport<ProgressReport> report; 01520 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) ); 01521 progress.name(str::form(_("Adding repository '%s'"), info.label().c_str())); 01522 progress.toMin(); 01523 01524 MIL << "Try adding repo " << info << endl; 01525 01526 RepoInfo tosave = info; 01527 if(_pimpl->repos.find(tosave)!= _pimpl->repos.end()) 01528 ZYPP_THROW(RepoAlreadyExistsException(info)); 01529 01530 // check the first url for now 01531 if ( _pimpl->options.probe ) 01532 { 01533 DBG << "unknown repository type, probing" << endl; 01534 01535 RepoType probedtype; 01536 probedtype = probe( *tosave.baseUrlsBegin(), info.path() ); 01537 if ( tosave.baseUrlsSize() > 0 ) 01538 { 01539 if ( probedtype == RepoType::NONE ) 01540 ZYPP_THROW(RepoUnknownTypeException()); 01541 else 01542 tosave.setType(probedtype); 01543 } 01544 } 01545 01546 progress.set(50); 01547 01548 // assert the directory exists 01549 filesystem::assert_dir(_pimpl->options.knownReposPath); 01550 01551 Pathname repofile = _pimpl->generateNonExistingName( 01552 _pimpl->options.knownReposPath, _pimpl->generateFilename(tosave)); 01553 // now we have a filename that does not exists 01554 MIL << "Saving repo in " << repofile << endl; 01555 01556 std::ofstream file(repofile.c_str()); 01557 if (!file) 01558 { 01559 // TranslatorExplanation '%s' is a filename 01560 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() ))); 01561 } 01562 01563 tosave.dumpAsIniOn(file); 01564 tosave.setFilepath(repofile); 01565 tosave.setMetadataPath( metadataPath( tosave ) ); 01566 tosave.setPackagesPath( packagesPath( tosave ) ); 01567 { 01568 // We chould fix the API as we must injet those paths 01569 // into the repoinfo in order to keep it usable. 01570 RepoInfo & oinfo( const_cast<RepoInfo &>(info) ); 01571 oinfo.setMetadataPath( metadataPath( tosave ) ); 01572 oinfo.setPackagesPath( packagesPath( tosave ) ); 01573 } 01574 _pimpl->repos.insert(tosave); 01575 01576 progress.set(90); 01577 01578 // check for credentials in Urls 01579 UrlCredentialExtractor( _pimpl->options.rootDir ).collect( tosave.baseUrls() ); 01580 01581 HistoryLog(_pimpl->options.rootDir).addRepository(tosave); 01582 01583 progress.toMax(); 01584 MIL << "done" << endl; 01585 } 01586 01587 void RepoManager::addRepositories( const Url &url, 01588 const ProgressData::ReceiverFnc & progressrcv ) 01589 { 01590 std::list<RepoInfo> repos = readRepoFile(url); 01591 for ( std::list<RepoInfo>::const_iterator it = repos.begin(); 01592 it != repos.end(); 01593 ++it ) 01594 { 01595 // look if the alias is in the known repos. 01596 for_ ( kit, repoBegin(), repoEnd() ) 01597 { 01598 if ( (*it).alias() == (*kit).alias() ) 01599 { 01600 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl; 01601 ZYPP_THROW(RepoAlreadyExistsException(*it)); 01602 } 01603 } 01604 } 01605 01606 std::string filename = Pathname(url.getPathName()).basename(); 01607 01608 if ( filename == Pathname() ) 01609 { 01610 // TranslatorExplanation '%s' is an URL 01611 ZYPP_THROW(RepoException(str::form( _("Invalid repo file name at '%s'"), url.asString().c_str() ))); 01612 } 01613 01614 // assert the directory exists 01615 filesystem::assert_dir(_pimpl->options.knownReposPath); 01616 01617 Pathname repofile = _pimpl->generateNonExistingName(_pimpl->options.knownReposPath, filename); 01618 // now we have a filename that does not exists 01619 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl; 01620 01621 std::ofstream file(repofile.c_str()); 01622 if (!file) 01623 { 01624 // TranslatorExplanation '%s' is a filename 01625 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() ))); 01626 } 01627 01628 for ( std::list<RepoInfo>::iterator it = repos.begin(); 01629 it != repos.end(); 01630 ++it ) 01631 { 01632 MIL << "Saving " << (*it).alias() << endl; 01633 it->setFilepath(repofile.asString()); 01634 it->dumpAsIniOn(file); 01635 _pimpl->repos.insert(*it); 01636 01637 HistoryLog(_pimpl->options.rootDir).addRepository(*it); 01638 } 01639 01640 MIL << "done" << endl; 01641 } 01642 01644 01645 void RepoManager::removeRepository( const RepoInfo & info, 01646 const ProgressData::ReceiverFnc & progressrcv) 01647 { 01648 ProgressData progress; 01649 callback::SendReport<ProgressReport> report; 01650 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) ); 01651 progress.name(str::form(_("Removing repository '%s'"), info.label().c_str())); 01652 01653 MIL << "Going to delete repo " << info.alias() << endl; 01654 01655 for_( it, repoBegin(), repoEnd() ) 01656 { 01657 // they can be the same only if the provided is empty, that means 01658 // the provided repo has no alias 01659 // then skip 01660 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) ) 01661 continue; 01662 01663 // TODO match by url 01664 01665 // we have a matcing repository, now we need to know 01666 // where it does come from. 01667 RepoInfo todelete = *it; 01668 if (todelete.filepath().empty()) 01669 { 01670 ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") )); 01671 } 01672 else 01673 { 01674 // figure how many repos are there in the file: 01675 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath()); 01676 if ( filerepos.size() == 0 // bsc#984494: file may have already been deleted 01677 ||(filerepos.size() == 1 && filerepos.front().alias() == todelete.alias() ) ) 01678 { 01679 // easy: file does not exist, contains no or only the repo to delete: delete the file 01680 int ret = filesystem::unlink( todelete.filepath() ); 01681 if ( ! ( ret == 0 || ret == ENOENT ) ) 01682 { 01683 // TranslatorExplanation '%s' is a filename 01684 ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), todelete.filepath().c_str() ))); 01685 } 01686 MIL << todelete.alias() << " sucessfully deleted." << endl; 01687 } 01688 else 01689 { 01690 // there are more repos in the same file 01691 // write them back except the deleted one. 01692 //TmpFile tmp; 01693 //std::ofstream file(tmp.path().c_str()); 01694 01695 // assert the directory exists 01696 filesystem::assert_dir(todelete.filepath().dirname()); 01697 01698 std::ofstream file(todelete.filepath().c_str()); 01699 if (!file) 01700 { 01701 // TranslatorExplanation '%s' is a filename 01702 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() ))); 01703 } 01704 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin(); 01705 fit != filerepos.end(); 01706 ++fit ) 01707 { 01708 if ( (*fit).alias() != todelete.alias() ) 01709 (*fit).dumpAsIniOn(file); 01710 } 01711 } 01712 01713 CombinedProgressData subprogrcv(progress, 70); 01714 CombinedProgressData cleansubprogrcv(progress, 30); 01715 // now delete it from cache 01716 if ( isCached(todelete) ) 01717 cleanCache( todelete, subprogrcv); 01718 // now delete metadata (#301037) 01719 cleanMetadata( todelete, cleansubprogrcv); 01720 _pimpl->repos.erase(todelete); 01721 MIL << todelete.alias() << " sucessfully deleted." << endl; 01722 HistoryLog(_pimpl->options.rootDir).removeRepository(todelete); 01723 return; 01724 } // else filepath is empty 01725 01726 } 01727 // should not be reached on a sucess workflow 01728 ZYPP_THROW(RepoNotFoundException(info)); 01729 } 01730 01732 01733 void RepoManager::modifyRepository( const std::string &alias, 01734 const RepoInfo & newinfo_r, 01735 const ProgressData::ReceiverFnc & progressrcv ) 01736 { 01737 RepoInfo toedit = getRepositoryInfo(alias); 01738 RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data 01739 01740 // check if the new alias already exists when renaming the repo 01741 if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) ) 01742 { 01743 ZYPP_THROW(RepoAlreadyExistsException(newinfo)); 01744 } 01745 01746 if (toedit.filepath().empty()) 01747 { 01748 ZYPP_THROW(RepoException( _("Can't figure out where the repo is stored.") )); 01749 } 01750 else 01751 { 01752 // figure how many repos are there in the file: 01753 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath()); 01754 01755 // there are more repos in the same file 01756 // write them back except the deleted one. 01757 //TmpFile tmp; 01758 //std::ofstream file(tmp.path().c_str()); 01759 01760 // assert the directory exists 01761 filesystem::assert_dir(toedit.filepath().dirname()); 01762 01763 std::ofstream file(toedit.filepath().c_str()); 01764 if (!file) 01765 { 01766 // TranslatorExplanation '%s' is a filename 01767 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() ))); 01768 } 01769 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin(); 01770 fit != filerepos.end(); 01771 ++fit ) 01772 { 01773 // if the alias is different, dump the original 01774 // if it is the same, dump the provided one 01775 if ( (*fit).alias() != toedit.alias() ) 01776 (*fit).dumpAsIniOn(file); 01777 else 01778 newinfo.dumpAsIniOn(file); 01779 } 01780 01781 newinfo.setFilepath(toedit.filepath()); 01782 _pimpl->repos.erase(toedit); 01783 _pimpl->repos.insert(newinfo); 01784 // check for credentials in Urls 01785 UrlCredentialExtractor( _pimpl->options.rootDir ).collect( newinfo.baseUrls() ); 01786 HistoryLog(_pimpl->options.rootDir).modifyRepository(toedit, newinfo); 01787 MIL << "repo " << alias << " modified" << endl; 01788 } 01789 } 01790 01792 01793 RepoInfo RepoManager::getRepositoryInfo( const std::string &alias, 01794 const ProgressData::ReceiverFnc & progressrcv ) 01795 { 01796 RepoInfo info; 01797 info.setAlias(alias); 01798 RepoConstIterator it = _pimpl->repos.find( info ); 01799 if( it == repoEnd() ) 01800 ZYPP_THROW(RepoNotFoundException(info)); 01801 else 01802 return *it; 01803 } 01804 01806 01807 RepoInfo RepoManager::getRepositoryInfo( const Url & url, 01808 const url::ViewOption & urlview, 01809 const ProgressData::ReceiverFnc & progressrcv ) 01810 { 01811 for_( it, repoBegin(), repoEnd() ) 01812 { 01813 for(RepoInfo::urls_const_iterator urlit = (*it).baseUrlsBegin(); 01814 urlit != (*it).baseUrlsEnd(); 01815 ++urlit) 01816 { 01817 if ((*urlit).asString(urlview) == url.asString(urlview)) 01818 return *it; 01819 } 01820 } 01821 RepoInfo info; 01822 info.setBaseUrl(url); 01823 ZYPP_THROW(RepoNotFoundException(info)); 01824 } 01825 01827 // 01828 // Services 01829 // 01831 01832 bool RepoManager::serviceEmpty() const 01833 { return _pimpl->services.empty(); } 01834 01835 RepoManager::ServiceSizeType RepoManager::serviceSize() const 01836 { return _pimpl->services.size(); } 01837 01838 RepoManager::ServiceConstIterator RepoManager::serviceBegin() const 01839 { return _pimpl->services.begin(); } 01840 01841 RepoManager::ServiceConstIterator RepoManager::serviceEnd() const 01842 { return _pimpl->services.end(); } 01843 01844 ServiceInfo RepoManager::getService( const std::string & alias ) const 01845 { 01846 for_( it, serviceBegin(), serviceEnd() ) 01847 if ( it->alias() == alias ) 01848 return *it; 01849 return ServiceInfo::noService; 01850 } 01851 01852 bool RepoManager::hasService( const std::string & alias ) const 01853 { 01854 for_( it, serviceBegin(), serviceEnd() ) 01855 if ( it->alias() == alias ) 01856 return true; 01857 return false; 01858 } 01859 01861 01862 void RepoManager::addService( const std::string & alias, const Url & url ) 01863 { 01864 addService( ServiceInfo(alias, url) ); 01865 } 01866 01867 void RepoManager::addService( const ServiceInfo & service ) 01868 { 01869 assert_alias( service ); 01870 01871 // check if service already exists 01872 if ( hasService( service.alias() ) ) 01873 ZYPP_THROW( ServiceAlreadyExistsException( service ) ); 01874 01875 // Writable ServiceInfo is needed to save the location 01876 // of the .service file. Finaly insert into the service list. 01877 ServiceInfo toSave( service ); 01878 _pimpl->saveService( toSave ); 01879 _pimpl->services.insert( toSave ); 01880 01881 // check for credentials in Url 01882 UrlCredentialExtractor( _pimpl->options.rootDir ).collect( toSave.url() ); 01883 01884 MIL << "added service " << toSave.alias() << endl; 01885 } 01886 01888 01889 void RepoManager::removeService( const std::string & alias ) 01890 { 01891 MIL << "Going to delete service " << alias << endl; 01892 01893 const ServiceInfo & service = getService( alias ); 01894 01895 Pathname location = service.filepath(); 01896 if( location.empty() ) 01897 { 01898 ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") )); 01899 } 01900 01901 ServiceSet tmpSet; 01902 parser::ServiceFileReader( location, ServiceCollector(tmpSet) ); 01903 01904 // only one service definition in the file 01905 if ( tmpSet.size() == 1 ) 01906 { 01907 if ( filesystem::unlink(location) != 0 ) 01908 { 01909 // TranslatorExplanation '%s' is a filename 01910 ZYPP_THROW(RepoException(str::form( _("Can't delete '%s'"), location.c_str() ))); 01911 } 01912 MIL << alias << " sucessfully deleted." << endl; 01913 } 01914 else 01915 { 01916 filesystem::assert_dir(location.dirname()); 01917 01918 std::ofstream file(location.c_str()); 01919 if( !file ) 01920 { 01921 // TranslatorExplanation '%s' is a filename 01922 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), location.c_str() ))); 01923 } 01924 01925 for_(it, tmpSet.begin(), tmpSet.end()) 01926 { 01927 if( it->alias() != alias ) 01928 it->dumpAsIniOn(file); 01929 } 01930 01931 MIL << alias << " sucessfully deleted from file " << location << endl; 01932 } 01933 01934 // now remove all repositories added by this service 01935 RepoCollector rcollector; 01936 getRepositoriesInService( alias, 01937 boost::make_function_output_iterator( 01938 bind( &RepoCollector::collect, &rcollector, _1 ) ) ); 01939 // cannot do this directly in getRepositoriesInService - would invalidate iterators 01940 for_(rit, rcollector.repos.begin(), rcollector.repos.end()) 01941 removeRepository(*rit); 01942 } 01943 01944 void RepoManager::removeService( const ServiceInfo & service ) 01945 { removeService(service.alias()); } 01946 01948 01949 void RepoManager::refreshServices() 01950 { 01951 // copy the set of services since refreshService 01952 // can eventually invalidate the iterator 01953 ServiceSet services( serviceBegin(), serviceEnd() ); 01954 for_( it, services.begin(), services.end() ) 01955 { 01956 if ( !it->enabled() ) 01957 continue; 01958 01959 try { 01960 refreshService(*it); 01961 } 01962 catch ( const repo::ServicePluginInformalException & e ) 01963 { ;/* ignore ServicePluginInformalException */ } 01964 } 01965 } 01966 01967 void RepoManager::refreshService( const ServiceInfo & service ) 01968 { refreshService( service.alias() ); } 01969 01970 void RepoManager::refreshService( const std::string & alias ) 01971 { 01972 ServiceInfo service( getService( alias ) ); 01973 assert_alias( service ); 01974 assert_url( service ); 01975 // NOTE: It might be necessary to modify and rewrite the service info. 01976 // Either when probing the type, or when adjusting the repositories 01977 // enable/disable state.: 01978 bool serviceModified = false; 01979 MIL << "Going to refresh service '" << service.alias() << "', url: "<< service.url() << endl; 01980 01982 01983 // if the type is unknown, try probing. 01984 if ( service.type() == repo::ServiceType::NONE ) 01985 { 01986 repo::ServiceType type = probeService( service.url() ); 01987 if ( type != ServiceType::NONE ) 01988 { 01989 service.setProbedType( type ); // lazy init! 01990 serviceModified = true; 01991 } 01992 } 01993 01994 // get target distro identifier 01995 std::string servicesTargetDistro = _pimpl->options.servicesTargetDistro; 01996 if ( servicesTargetDistro.empty() ) 01997 { 01998 servicesTargetDistro = Target::targetDistribution( Pathname() ); 01999 } 02000 DBG << "ServicesTargetDistro: " << servicesTargetDistro << endl; 02001 02002 // parse it 02003 RepoCollector collector(servicesTargetDistro); 02004 // FIXME Ugly hack: ServiceRepos may throw ServicePluginInformalException 02005 // which is actually a notification. Using an exception for this 02006 // instead of signal/callback is bad. Needs to be fixed here, in refreshServices() 02007 // and in zypper. 02008 std::pair<DefaultIntegral<bool,false>, repo::ServicePluginInformalException> uglyHack; 02009 try { 02010 ServiceRepos repos(service, bind( &RepoCollector::collect, &collector, _1 )); 02011 } 02012 catch ( const repo::ServicePluginInformalException & e ) 02013 { 02014 /* ignore ServicePluginInformalException and throw later */ 02015 uglyHack.first = true; 02016 uglyHack.second = e; 02017 } 02018 02020 // On the fly remember the new repo states as defined the reopoindex.xml. 02021 // Move into ServiceInfo later. 02022 ServiceInfo::RepoStates newRepoStates; 02023 02024 // set service alias and base url for all collected repositories 02025 for_( it, collector.repos.begin(), collector.repos.end() ) 02026 { 02027 // First of all: Prepend service alias: 02028 it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) ); 02029 // set refrence to the parent service 02030 it->setService( service.alias() ); 02031 02032 // remember the new parsed repo state 02033 newRepoStates[it->alias()] = *it; 02034 02035 // - If the repo url was not set by the repoindex parser, set service's url. 02036 // - Libzypp currently has problem with separate url + path handling so just 02037 // append a path, if set, to the baseurls 02038 // - Credentials in the url authority will be extracted later, either if the 02039 // repository is added or if we check for changed urls. 02040 Pathname path; 02041 if ( !it->path().empty() ) 02042 { 02043 if ( it->path() != "/" ) 02044 path = it->path(); 02045 it->setPath(""); 02046 } 02047 02048 if ( it->baseUrlsEmpty() ) 02049 { 02050 Url url( service.url() ); 02051 if ( !path.empty() ) 02052 url.setPathName( url.getPathName() / path ); 02053 it->setBaseUrl( url ); 02054 } 02055 else if ( !path.empty() ) 02056 { 02057 RepoInfo::url_set urls; 02058 for_( urlit, it->baseUrlsBegin(), it->baseUrlsEnd() ) 02059 { 02060 Url url( *urlit ); 02061 url.setPathName( url.getPathName() / path ); 02062 urls.insert( url ); 02063 } 02064 it->setBaseUrls( urls ); 02065 } 02066 } 02067 02069 // Now compare collected repos with the ones in the system... 02070 // 02071 RepoInfoList oldRepos; 02072 getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) ); 02073 02075 // find old repositories to remove... 02076 for_( oldRepo, oldRepos.begin(), oldRepos.end() ) 02077 { 02078 if ( ! foundAliasIn( oldRepo->alias(), collector.repos ) ) 02079 { 02080 if ( oldRepo->enabled() ) 02081 { 02082 // Currently enabled. If this was a user modification remember the state. 02083 ServiceInfo::RepoStates::const_iterator last = service.repoStates().find( oldRepo->alias() ); 02084 if ( last != service.repoStates().end() && ! last->second.enabled ) 02085 { 02086 DBG << "Service removes user enabled repo " << oldRepo->alias() << endl; 02087 service.addRepoToEnable( oldRepo->alias() ); 02088 serviceModified = true; 02089 } 02090 else 02091 DBG << "Service removes enabled repo " << oldRepo->alias() << endl; 02092 } 02093 else 02094 DBG << "Service removes disabled repo " << oldRepo->alias() << endl; 02095 02096 removeRepository( *oldRepo ); 02097 } 02098 } 02099 02101 // create missing repositories and modify existing ones if needed... 02102 UrlCredentialExtractor urlCredentialExtractor( _pimpl->options.rootDir ); // To collect any credentials stored in repo URLs 02103 for_( it, collector.repos.begin(), collector.repos.end() ) 02104 { 02105 // User explicitly requested the repo being enabled? 02106 // User explicitly requested the repo being disabled? 02107 // And hopefully not both ;) If so, enable wins. 02108 02109 TriBool toBeEnabled( indeterminate ); // indeterminate - follow the service request 02110 DBG << "Service request to " << (it->enabled()?"enable":"disable") << " service repo " << it->alias() << endl; 02111 02112 if ( service.repoToEnableFind( it->alias() ) ) 02113 { 02114 DBG << "User request to enable service repo " << it->alias() << endl; 02115 toBeEnabled = true; 02116 // Remove from enable request list. 02117 // NOTE: repoToDisable is handled differently. 02118 // It gets cleared on each refresh. 02119 service.delRepoToEnable( it->alias() ); 02120 serviceModified = true; 02121 } 02122 else if ( service.repoToDisableFind( it->alias() ) ) 02123 { 02124 DBG << "User request to disable service repo " << it->alias() << endl; 02125 toBeEnabled = false; 02126 } 02127 02128 02129 RepoInfoList::iterator oldRepo( findAlias( it->alias(), oldRepos ) ); 02130 if ( oldRepo == oldRepos.end() ) 02131 { 02132 // Not found in oldRepos ==> a new repo to add 02133 02134 // Make sure the service repo is created with the appropriate enablement 02135 if ( ! indeterminate(toBeEnabled) ) 02136 it->setEnabled( toBeEnabled ); 02137 02138 DBG << "Service adds repo " << it->alias() << " " << (it->enabled()?"enabled":"disabled") << endl; 02139 addRepository( *it ); 02140 } 02141 else 02142 { 02143 // ==> an exising repo to check 02144 bool oldRepoModified = false; 02145 02146 if ( indeterminate(toBeEnabled) ) 02147 { 02148 // No user request: check for an old user modificaton otherwise follow service request. 02149 // NOTE: Assert toBeEnabled is boolean afterwards! 02150 if ( oldRepo->enabled() == it->enabled() ) 02151 toBeEnabled = it->enabled(); // service requests no change to the system 02152 else 02153 { 02154 ServiceInfo::RepoStates::const_iterator last = service.repoStates().find( oldRepo->alias() ); 02155 if ( last == service.repoStates().end() || last->second.enabled != it->enabled() ) 02156 toBeEnabled = it->enabled(); // service request has changed since last refresh -> follow 02157 else 02158 { 02159 toBeEnabled = oldRepo->enabled(); // service request unchaned since last refresh -> keep user modification 02160 DBG << "User modified service repo " << it->alias() << " may stay " << (toBeEnabled?"enabled":"disabled") << endl; 02161 } 02162 } 02163 } 02164 02165 // changed enable? 02166 if ( toBeEnabled == oldRepo->enabled() ) 02167 { 02168 DBG << "Service repo " << it->alias() << " stays " << (oldRepo->enabled()?"enabled":"disabled") << endl; 02169 } 02170 else if ( toBeEnabled ) 02171 { 02172 DBG << "Service repo " << it->alias() << " gets enabled" << endl; 02173 oldRepo->setEnabled( true ); 02174 oldRepoModified = true; 02175 } 02176 else 02177 { 02178 DBG << "Service repo " << it->alias() << " gets disabled" << endl; 02179 oldRepo->setEnabled( false ); 02180 oldRepoModified = true; 02181 } 02182 02183 // all other attributes follow the service request: 02184 02185 // changed autorefresh 02186 if ( oldRepo->autorefresh() != it->autorefresh() ) 02187 { 02188 DBG << "Service repo " << it->alias() << " gets new AUTOREFRESH " << it->autorefresh() << endl; 02189 oldRepo->setAutorefresh( it->autorefresh() ); 02190 oldRepoModified = true; 02191 } 02192 02193 // changed priority? 02194 if ( oldRepo->priority() != it->priority() ) 02195 { 02196 DBG << "Service repo " << it->alias() << " gets new PRIORITY " << it->priority() << endl; 02197 oldRepo->setPriority( it->priority() ); 02198 oldRepoModified = true; 02199 } 02200 02201 // changed url? 02202 { 02203 RepoInfo::url_set newUrls; 02204 for_( urlit, it->baseUrlsBegin(), it->baseUrlsEnd() ) 02205 { 02206 Url url( *urlit ); 02207 urlCredentialExtractor.extract( url ); // Extract! to prevent passwds from disturbing the comparison below 02208 newUrls.insert( url ); 02209 } 02210 02211 if ( oldRepo->baseUrls() != newUrls ) 02212 { 02213 DBG << "Service repo " << it->alias() << " gets new URLs " << newUrls << endl; 02214 oldRepo->setBaseUrls( newUrls ); 02215 oldRepoModified = true; 02216 } 02217 } 02218 02219 // save if modified: 02220 if ( oldRepoModified ) 02221 { 02222 modifyRepository( oldRepo->alias(), *oldRepo ); 02223 } 02224 } 02225 } 02226 02227 // Unlike reposToEnable, reposToDisable is always cleared after refresh. 02228 if ( ! service.reposToDisableEmpty() ) 02229 { 02230 service.clearReposToDisable(); 02231 serviceModified = true; 02232 } 02233 02234 // Remember original service request for next refresh 02235 if ( service.repoStates() != newRepoStates ) 02236 { 02237 service.setRepoStates( newRepoStates ); 02238 serviceModified = true; 02239 } 02240 02242 // save service if modified: (unless a plugin service) 02243 if ( serviceModified && service.type() != ServiceType::PLUGIN ) 02244 { 02245 // write out modified service file. 02246 modifyService( service.alias(), service ); 02247 } 02248 02249 if ( uglyHack.first ) 02250 { 02251 throw( uglyHack.second ); // intentionally not ZYPP_THROW 02252 } 02253 } 02254 02256 02257 void RepoManager::modifyService(const std::string & oldAlias, const ServiceInfo & newService) 02258 { 02259 MIL << "Going to modify service " << oldAlias << endl; 02260 02261 // we need a writable copy to link it to the file where 02262 // it is saved if we modify it 02263 ServiceInfo service(newService); 02264 02265 if ( service.type() == ServiceType::PLUGIN ) 02266 { 02267 WAR << "Not modifying plugin service '" << oldAlias << "'" << endl; 02268 return; 02269 } 02270 02271 const ServiceInfo & oldService = getService(oldAlias); 02272 02273 Pathname location = oldService.filepath(); 02274 if( location.empty() ) 02275 { 02276 ZYPP_THROW(RepoException( _("Can't figure out where the service is stored.") )); 02277 } 02278 02279 // remember: there may multiple services being defined in one file: 02280 ServiceSet tmpSet; 02281 parser::ServiceFileReader( location, ServiceCollector(tmpSet) ); 02282 02283 filesystem::assert_dir(location.dirname()); 02284 std::ofstream file(location.c_str()); 02285 for_(it, tmpSet.begin(), tmpSet.end()) 02286 { 02287 if( *it != oldAlias ) 02288 it->dumpAsIniOn(file); 02289 } 02290 service.dumpAsIniOn(file); 02291 file.close(); 02292 service.setFilepath(location); 02293 02294 _pimpl->services.erase(oldAlias); 02295 _pimpl->services.insert(service); 02296 // check for credentials in Urls 02297 UrlCredentialExtractor( _pimpl->options.rootDir ).collect( service.url() ); 02298 02299 // changed properties affecting also repositories 02300 if ( oldAlias != service.alias() // changed alias 02301 || oldService.enabled() != service.enabled() ) // changed enabled status 02302 { 02303 std::vector<RepoInfo> toModify; 02304 getRepositoriesInService(oldAlias, std::back_inserter(toModify)); 02305 for_( it, toModify.begin(), toModify.end() ) 02306 { 02307 if ( oldService.enabled() != service.enabled() ) 02308 { 02309 if ( service.enabled() ) 02310 { 02311 // reset to last refreshs state 02312 ServiceInfo::RepoStates::const_iterator last = service.repoStates().find( it->alias() ); 02313 if ( last != service.repoStates().end() ) 02314 it->setEnabled( last->second.enabled ); 02315 } 02316 else 02317 it->setEnabled( false ); 02318 } 02319 02320 if ( oldAlias != service.alias() ) 02321 it->setService(service.alias()); 02322 02323 modifyRepository(it->alias(), *it); 02324 } 02325 } 02326 02328 } 02329 02331 02332 repo::ServiceType RepoManager::probeService( const Url &url ) const 02333 { 02334 try 02335 { 02336 MediaSetAccess access(url); 02337 if ( access.doesFileExist("/repo/repoindex.xml") ) 02338 return repo::ServiceType::RIS; 02339 } 02340 catch ( const media::MediaException &e ) 02341 { 02342 ZYPP_CAUGHT(e); 02343 // TranslatorExplanation '%s' is an URL 02344 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() )); 02345 enew.remember(e); 02346 ZYPP_THROW(enew); 02347 } 02348 catch ( const Exception &e ) 02349 { 02350 ZYPP_CAUGHT(e); 02351 // TranslatorExplanation '%s' is an URL 02352 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() )); 02353 enew.remember(e); 02354 ZYPP_THROW(enew); 02355 } 02356 02357 return repo::ServiceType::NONE; 02358 } 02359 02361 02362 std::ostream & operator<<( std::ostream & str, const RepoManager & obj ) 02363 { 02364 return str << *obj._pimpl; 02365 } 02366 02368 } // namespace zypp