libzypp 17.31.23
RepoManager.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
13#include <cstdlib>
14#include <iostream>
15#include <fstream>
16#include <sstream>
17#include <list>
18#include <map>
19#include <algorithm>
20#include <chrono>
21
22#include <solv/solvversion.h>
23
24#include <zypp-core/base/InputStream>
25#include <zypp/base/LogTools.h>
26#include <zypp/base/Gettext.h>
27#include <zypp-core/base/DefaultIntegral>
28#include <zypp/base/Function.h>
29#include <zypp/base/Regex.h>
30#include <zypp/PathInfo.h>
31#include <zypp/TmpPath.h>
32
33#include <zypp/ServiceInfo.h>
35#include <zypp/RepoManager.h>
36
38#include <zypp-media/auth/CredentialManager>
39#include <zypp-media/MediaException>
40#include <zypp/MediaSetAccess.h>
41#include <zypp/ExternalProgram.h>
42#include <zypp/ManagedFile.h>
43
48#include <zypp/repo/yum/Downloader.h>
49#include <zypp/repo/susetags/Downloader.h>
52
53#include <zypp/Target.h> // for Target::targetDistribution() for repo index services
54#include <zypp/ZYppFactory.h> // to get the Target from ZYpp instance
55#include <zypp/HistoryLog.h> // to write history :O)
56
57#include <zypp/ZYppCallbacks.h>
58
59#include "sat/Pool.h"
60#include <zypp/base/Algorithm.h>
61
62using std::endl;
63using std::string;
64using namespace zypp::repo;
65
66#define OPT_PROGRESS const ProgressData::ReceiverFnc & = ProgressData::ReceiverFnc()
67
69namespace zypp
70{
71 namespace zypp_readonly_hack {
72 bool IGotIt(); // in readonly-mode
73 }
74
76 namespace env
77 {
80 {
81 const char * env = getenv("ZYPP_PLUGIN_APPDATA_FORCE_COLLECT");
82 return( env && str::strToBool( env, true ) );
83 }
84 } // namespace env
86
88 namespace
89 {
111 class UrlCredentialExtractor
112 {
113 public:
114 UrlCredentialExtractor( Pathname & root_r )
115 : _root( root_r )
116 {}
117
118 ~UrlCredentialExtractor()
119 { if ( _cmPtr ) _cmPtr->save(); }
120
122 bool collect( const Url & url_r )
123 {
124 bool ret = url_r.hasCredentialsInAuthority();
125 if ( ret )
126 {
127 if ( !_cmPtr ) _cmPtr.reset( new media::CredentialManager( _root ) );
128 _cmPtr->addUserCred( url_r );
129 }
130 return ret;
131 }
133 template<class TContainer>
134 bool collect( const TContainer & urls_r )
135 { bool ret = false; for ( const Url & url : urls_r ) { if ( collect( url ) && !ret ) ret = true; } return ret; }
136
138 bool extract( Url & url_r )
139 {
140 bool ret = collect( url_r );
141 if ( ret )
142 url_r.setPassword( std::string() );
143 return ret;
144 }
146 template<class TContainer>
147 bool extract( TContainer & urls_r )
148 { bool ret = false; for ( Url & url : urls_r ) { if ( extract( url ) && !ret ) ret = true; } return ret; }
149
150 private:
153 };
154 } // namespace
156
158 namespace
159 {
163 class MediaMounter
164 {
165 public:
167 MediaMounter( const Url & url_r )
168 {
169 media::MediaManager mediamanager;
170 _mid = mediamanager.open( url_r );
171 mediamanager.attach( _mid );
172 }
173
175 ~MediaMounter()
176 {
177 media::MediaManager mediamanager;
178 mediamanager.release( _mid );
179 mediamanager.close( _mid );
180 }
181
186 Pathname getPathName( const Pathname & path_r = Pathname() ) const
187 {
188 media::MediaManager mediamanager;
189 return mediamanager.localPath( _mid, path_r );
190 }
191
192 private:
194 };
196
198 template <class Iterator>
199 inline bool foundAliasIn( const std::string & alias_r, Iterator begin_r, Iterator end_r )
200 {
201 for_( it, begin_r, end_r )
202 if ( it->alias() == alias_r )
203 return true;
204 return false;
205 }
207 template <class Container>
208 inline bool foundAliasIn( const std::string & alias_r, const Container & cont_r )
209 { return foundAliasIn( alias_r, cont_r.begin(), cont_r.end() ); }
210
212 template <class Iterator>
213 inline Iterator findAlias( const std::string & alias_r, Iterator begin_r, Iterator end_r )
214 {
215 for_( it, begin_r, end_r )
216 if ( it->alias() == alias_r )
217 return it;
218 return end_r;
219 }
221 template <class Container>
222 inline typename Container::iterator findAlias( const std::string & alias_r, Container & cont_r )
223 { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); }
225 template <class Container>
226 inline typename Container::const_iterator findAlias( const std::string & alias_r, const Container & cont_r )
227 { return findAlias( alias_r, cont_r.begin(), cont_r.end() ); }
228
229
231 inline std::string filenameFromAlias( const std::string & alias_r, const std::string & stem_r )
232 {
233 std::string filename( alias_r );
234 // replace slashes with underscores
235 str::replaceAll( filename, "/", "_" );
236
237 filename = Pathname(filename).extend("."+stem_r).asString();
238 MIL << "generating filename for " << stem_r << " [" << alias_r << "] : '" << filename << "'" << endl;
239 return filename;
240 }
241
257 struct RepoCollector : private base::NonCopyable
258 {
259 RepoCollector()
260 {}
261
262 RepoCollector(const std::string & targetDistro_)
263 : targetDistro(targetDistro_)
264 {}
265
266 bool collect( const RepoInfo &repo )
267 {
268 // skip repositories meant for other distros than specified
269 if (!targetDistro.empty()
270 && !repo.targetDistribution().empty()
271 && repo.targetDistribution() != targetDistro)
272 {
273 MIL
274 << "Skipping repository meant for '" << repo.targetDistribution()
275 << "' distribution (current distro is '"
276 << targetDistro << "')." << endl;
277
278 return true;
279 }
280
281 repos.push_back(repo);
282 return true;
283 }
284
285 RepoInfoList repos;
286 std::string targetDistro;
287 };
289
295 std::list<RepoInfo> repositories_in_file( const Pathname & file )
296 {
297 MIL << "repo file: " << file << endl;
298 RepoCollector collector;
299 parser::RepoFileReader parser( file, bind( &RepoCollector::collect, &collector, _1 ) );
300 return std::move(collector.repos);
301 }
302
304
313 std::list<RepoInfo> repositories_in_dir( const Pathname &dir )
314 {
315 MIL << "directory " << dir << endl;
316 std::list<RepoInfo> repos;
317 bool nonroot( geteuid() != 0 );
318 if ( nonroot && ! PathInfo(dir).userMayRX() )
319 {
320 JobReport::warning( str::Format(_("Cannot read repo directory '%1%': Permission denied")) % dir );
321 }
322 else
323 {
324 std::list<Pathname> entries;
325 if ( filesystem::readdir( entries, dir, false ) != 0 )
326 {
327 // TranslatorExplanation '%s' is a pathname
328 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
329 }
330
331 str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
332 for ( std::list<Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
333 {
334 if ( str::regex_match(it->extension(), allowedRepoExt) )
335 {
336 if ( nonroot && ! PathInfo(*it).userMayR() )
337 {
338 JobReport::warning( str::Format(_("Cannot read repo file '%1%': Permission denied")) % *it );
339 }
340 else
341 {
342 const std::list<RepoInfo> & tmp( repositories_in_file( *it ) );
343 repos.insert( repos.end(), tmp.begin(), tmp.end() );
344 }
345 }
346 }
347 }
348 return repos;
349 }
350
352
353 inline void assert_alias( const RepoInfo & info )
354 {
355 if ( info.alias().empty() )
357 // bnc #473834. Maybe we can match the alias against a regex to define
358 // and check for valid aliases
359 if ( info.alias()[0] == '.')
361 info, _("Repository alias cannot start with dot.")));
362 }
363
364 inline void assert_alias( const ServiceInfo & info )
365 {
366 if ( info.alias().empty() )
368 // bnc #473834. Maybe we can match the alias against a regex to define
369 // and check for valid aliases
370 if ( info.alias()[0] == '.')
372 info, _("Service alias cannot start with dot.")));
373 }
374
376
377 inline void assert_urls( const RepoInfo & info )
378 {
379 if ( info.baseUrlsEmpty() )
381 }
382
383 inline void assert_url( const ServiceInfo & info )
384 {
385 if ( ! info.url().isValid() )
387 }
388
390
392 namespace
393 {
395 inline bool isTmpRepo( const RepoInfo & info_r )
396 { return( info_r.filepath().empty() && info_r.usesAutoMethadataPaths() ); }
397 } // namespace
399
404 inline Pathname rawcache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
405 {
406 assert_alias(info);
407 return isTmpRepo( info ) ? info.metadataPath() : opt.repoRawCachePath / info.escaped_alias();
408 }
409
418 inline Pathname rawproductdata_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
419 { return rawcache_path_for_repoinfo( opt, info ) / info.path(); }
420
424 inline Pathname packagescache_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
425 {
426 assert_alias(info);
427 return isTmpRepo( info ) ? info.packagesPath() : opt.repoPackagesCachePath / info.escaped_alias();
428 }
429
433 inline Pathname solv_path_for_repoinfo( const RepoManagerOptions &opt, const RepoInfo &info )
434 {
435 assert_alias(info);
436 return isTmpRepo( info ) ? info.metadataPath().dirname() / "%SLV%" : opt.repoSolvCachePath / info.escaped_alias();
437 }
438
440
442 class ServiceCollector
443 {
444 public:
445 typedef std::set<ServiceInfo> ServiceSet;
446
447 ServiceCollector( ServiceSet & services_r )
448 : _services( services_r )
449 {}
450
451 bool operator()( const ServiceInfo & service_r ) const
452 {
453 _services.insert( service_r );
454 return true;
455 }
456
457 private:
458 ServiceSet & _services;
459 };
461
463 inline bool autoPruneInDir( const Pathname & path_r )
464 { return not PathInfo(path_r/".no_auto_prune").isExist(); }
465
466 } // namespace
468
469 std::list<RepoInfo> readRepoFile( const Url & repo_file )
470 {
472
473 DBG << "reading repo file " << repo_file << ", local path: " << local << endl;
474
475 return repositories_in_file(local);
476 }
477
479 //
480 // class RepoManagerOptions
481 //
483
485 {
487 repoRawCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoMetadataPath() );
488 repoSolvCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoSolvfilesPath() );
489 repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() );
494
495 rootDir = root_r;
496 }
497
499 {
501 ret.repoCachePath = root_r;
502 ret.repoRawCachePath = root_r/"raw";
503 ret.repoSolvCachePath = root_r/"solv";
504 ret.repoPackagesCachePath = root_r/"packages";
505 ret.knownReposPath = root_r/"repos.d";
506 ret.knownServicesPath = root_r/"services.d";
507 ret.pluginsPath = root_r/"plugins";
508 ret.rootDir = root_r;
509 return ret;
510 }
511
512 std:: ostream & operator<<( std::ostream & str, const RepoManagerOptions & obj )
513 {
514#define OUTS(X) str << " " #X "\t" << obj.X << endl
515 str << "RepoManagerOptions (" << obj.rootDir << ") {" << endl;
516 OUTS( repoRawCachePath );
517 OUTS( repoSolvCachePath );
518 OUTS( repoPackagesCachePath );
519 OUTS( knownReposPath );
520 OUTS( knownServicesPath );
521 OUTS( pluginsPath );
522 str << "}" << endl;
523#undef OUTS
524 return str;
525 }
526
533 {
534 public:
536 : _options(opt)
537 , _pluginRepoverification( _options.pluginsPath/"repoverification", _options.rootDir )
538 {
539 init_knownServices();
540 init_knownRepositories();
541 }
542
544 {
545 // trigger appdata refresh if some repos change
546 if ( ( _reposDirty || env::ZYPP_PLUGIN_APPDATA_FORCE_COLLECT() )
547 && geteuid() == 0 && ( _options.rootDir.empty() || _options.rootDir == "/" ) )
548 {
549 try {
550 std::list<Pathname> entries;
551 filesystem::readdir( entries, _options.pluginsPath/"appdata", false );
552 if ( ! entries.empty() )
553 {
555 cmd.push_back( "<" ); // discard stdin
556 cmd.push_back( ">" ); // discard stdout
557 cmd.push_back( "PROGRAM" ); // [2] - fix index below if changing!
558 for ( const auto & rinfo : repos() )
559 {
560 if ( ! rinfo.enabled() )
561 continue;
562 cmd.push_back( "-R" );
563 cmd.push_back( rinfo.alias() );
564 cmd.push_back( "-t" );
565 cmd.push_back( rinfo.type().asString() );
566 cmd.push_back( "-p" );
567 cmd.push_back( (rinfo.metadataPath()/rinfo.path()).asString() ); // bsc#1197684: path to the repodata/ directory inside the cache
568 }
569
570 for_( it, entries.begin(), entries.end() )
571 {
572 PathInfo pi( *it );
573 //DBG << "/tmp/xx ->" << pi << endl;
574 if ( pi.isFile() && pi.userMayRX() )
575 {
576 // trigger plugin
577 cmd[2] = pi.asString(); // [2] - PROGRAM
578 ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
579 }
580 }
581 }
582 }
583 catch (...) {} // no throw in dtor
584 }
585 }
586
587 public:
588 bool repoEmpty() const { return repos().empty(); }
589 RepoSizeType repoSize() const { return repos().size(); }
590 RepoConstIterator repoBegin() const { return repos().begin(); }
591 RepoConstIterator repoEnd() const { return repos().end(); }
592
593 bool hasRepo( const std::string & alias ) const
594 { return foundAliasIn( alias, repos() ); }
595
596 RepoInfo getRepo( const std::string & alias ) const
597 {
598 RepoConstIterator it( findAlias( alias, repos() ) );
599 return it == repos().end() ? RepoInfo::noRepo : *it;
600 }
601
602 public:
603 Pathname metadataPath( const RepoInfo & info ) const
604 { return rawcache_path_for_repoinfo( _options, info ); }
605
606 Pathname packagesPath( const RepoInfo & info ) const
607 { return packagescache_path_for_repoinfo( _options, info ); }
608
609 RepoStatus metadataStatus( const RepoInfo & info ) const;
610
611 RefreshCheckStatus checkIfToRefreshMetadata( const RepoInfo & info, const Url & url, RawMetadataRefreshPolicy policy );
612
614
615 void cleanMetadata( const RepoInfo & info, OPT_PROGRESS );
616
617 void cleanPackages( const RepoInfo & info, OPT_PROGRESS, bool isAutoClean = false );
618
619 void buildCache( const RepoInfo & info, CacheBuildPolicy policy, OPT_PROGRESS );
620
621 repo::RepoType probe( const Url & url, const Pathname & path = Pathname() ) const;
622 repo::RepoType probeCache( const Pathname & path_r ) const;
623
625
626 void cleanCache( const RepoInfo & info, OPT_PROGRESS );
627
628 bool isCached( const RepoInfo & info ) const
629 { return PathInfo(solv_path_for_repoinfo( _options, info ) / "solv").isExist(); }
630
631 RepoStatus cacheStatus( const RepoInfo & info ) const
632 { return RepoStatus::fromCookieFile(solv_path_for_repoinfo(_options, info) / "cookie"); }
633
634 void loadFromCache( const RepoInfo & info, OPT_PROGRESS );
635
636 void addRepository( const RepoInfo & info, OPT_PROGRESS );
637
638 void addRepositories( const Url & url, OPT_PROGRESS );
639
641
642 void modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, OPT_PROGRESS );
643
644 RepoInfo getRepositoryInfo( const std::string & alias, OPT_PROGRESS );
646
647 public:
648 bool serviceEmpty() const { return _services.empty(); }
649 ServiceSizeType serviceSize() const { return _services.size(); }
650 ServiceConstIterator serviceBegin() const { return _services.begin(); }
651 ServiceConstIterator serviceEnd() const { return _services.end(); }
652
653 bool hasService( const std::string & alias ) const
654 { return foundAliasIn( alias, _services ); }
655
656 ServiceInfo getService( const std::string & alias ) const
657 {
658 ServiceConstIterator it( findAlias( alias, _services ) );
659 return it == _services.end() ? ServiceInfo::noService : *it;
660 }
661
662 public:
663 void addService( const ServiceInfo & service );
664 void addService( const std::string & alias, const Url & url )
665 { addService( ServiceInfo( alias, url ) ); }
666
667 void removeService( const std::string & alias );
668 void removeService( const ServiceInfo & service )
669 { removeService( service.alias() ); }
670
671 void refreshServices( const RefreshServiceOptions & options_r );
672
673 void refreshService( const std::string & alias, const RefreshServiceOptions & options_r );
674 void refreshService( const ServiceInfo & service, const RefreshServiceOptions & options_r )
675 { refreshService( service.alias(), options_r ); }
676
677 void modifyService( const std::string & oldAlias, const ServiceInfo & newService );
678
679 repo::ServiceType probeService( const Url & url ) const;
680
682
683 private:
684 void saveService( ServiceInfo & service ) const;
685
686 Pathname generateNonExistingName( const Pathname & dir, const std::string & basefilename ) const;
687
688 std::string generateFilename( const RepoInfo & info ) const
689 { return filenameFromAlias( info.alias(), "repo" ); }
690
691 std::string generateFilename( const ServiceInfo & info ) const
692 { return filenameFromAlias( info.alias(), "service" ); }
693
694 void setCacheStatus( const RepoInfo & info, const RepoStatus & status )
695 {
696 Pathname base = solv_path_for_repoinfo( _options, info );
697 filesystem::assert_dir(base);
698 status.saveToCookieFile( base / "cookie" );
699 }
700
701 void touchIndexFile( const RepoInfo & info );
702
703 template<typename OutputIterator>
704 void getRepositoriesInService( const std::string & alias, OutputIterator out ) const
705 {
706 MatchServiceAlias filter( alias );
707 std::copy( boost::make_filter_iterator( filter, repos().begin(), repos().end() ),
708 boost::make_filter_iterator( filter, repos().end(), repos().end() ),
709 out);
710 }
711
712 private:
715
716 const RepoSet & repos() const { return _reposX; }
717 RepoSet & reposManip() { if ( ! _reposDirty ) _reposDirty = true; return _reposX; }
718
719 private:
723
725
727
728 private:
729 friend Impl * rwcowClone<Impl>( const Impl * rhs );
731 Impl * clone() const
732 { return new Impl( *this ); }
733 };
735
737 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
738 { return str << "RepoManager::Impl"; }
739
741
742 void RepoManager::Impl::saveService( ServiceInfo & service ) const
743 {
744 filesystem::assert_dir( _options.knownServicesPath );
745 Pathname servfile = generateNonExistingName( _options.knownServicesPath,
746 generateFilename( service ) );
747 service.setFilepath( servfile );
748
749 MIL << "saving service in " << servfile << endl;
750
751 std::ofstream file( servfile.c_str() );
752 if ( !file )
753 {
754 // TranslatorExplanation '%s' is a filename
755 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
756 }
757 service.dumpAsIniOn( file );
758 MIL << "done" << endl;
759 }
760
776 Pathname RepoManager::Impl::generateNonExistingName( const Pathname & dir,
777 const std::string & basefilename ) const
778 {
779 std::string final_filename = basefilename;
780 int counter = 1;
781 while ( PathInfo(dir + final_filename).isExist() )
782 {
783 final_filename = basefilename + "_" + str::numstring(counter);
784 ++counter;
785 }
786 return dir + Pathname(final_filename);
787 }
788
790
791 void RepoManager::Impl::init_knownServices()
792 {
793 Pathname dir = _options.knownServicesPath;
794 std::list<Pathname> entries;
795 if (PathInfo(dir).isExist())
796 {
797 if ( filesystem::readdir( entries, dir, false ) != 0 )
798 {
799 // TranslatorExplanation '%s' is a pathname
800 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
801 }
802
803 //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
804 for_(it, entries.begin(), entries.end() )
805 {
806 parser::ServiceFileReader(*it, ServiceCollector(_services));
807 }
808 }
809
810 repo::PluginServices(_options.pluginsPath/"services", ServiceCollector(_services));
811 }
812
814 namespace {
821 inline void cleanupNonRepoMetadtaFolders( const Pathname & cachePath_r,
822 const Pathname & defaultCachePath_r,
823 const std::list<std::string> & repoEscAliases_r )
824 {
826 return;
827
828 if ( cachePath_r != defaultCachePath_r )
829 return;
830
831 std::list<std::string> entries;
832 if ( filesystem::readdir( entries, cachePath_r, false ) == 0 )
833 {
834 entries.sort();
835 std::set<std::string> oldfiles;
836 set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
837 std::inserter( oldfiles, oldfiles.end() ) );
838
839 // bsc#1178966: Files or symlinks here have been created by the user
840 // for whatever purpose. It's our cache, so we purge them now before
841 // they may later conflict with directories we need.
842 PathInfo pi;
843 for ( const std::string & old : oldfiles )
844 {
845 if ( old == Repository::systemRepoAlias() ) // don't remove the @System solv file
846 continue;
847 pi( cachePath_r/old );
848 if ( pi.isDir() )
850 else
851 filesystem::unlink( pi.path() );
852 }
853 }
854 }
855 } // namespace
857 void RepoManager::Impl::init_knownRepositories()
858 {
859 MIL << "start construct known repos" << endl;
860
861 if ( PathInfo(_options.knownReposPath).isExist() )
862 {
863 std::list<std::string> repoEscAliases;
864 std::list<RepoInfo> orphanedRepos;
865 for ( RepoInfo & repoInfo : repositories_in_dir(_options.knownReposPath) )
866 {
867 // set the metadata path for the repo
868 repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo) );
869 // set the downloaded packages path for the repo
870 repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo) );
871 // remember it
872 _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
873
874 // detect orphaned repos belonging to a deleted service
875 const std::string & serviceAlias( repoInfo.service() );
876 if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
877 {
878 WAR << "Schedule orphaned service repo for deletion: " << repoInfo << endl;
879 orphanedRepos.push_back( repoInfo );
880 continue; // don't remember it in repoEscAliases
881 }
882
883 repoEscAliases.push_back(repoInfo.escaped_alias());
884 }
885
886 // Cleanup orphanded service repos:
887 if ( ! orphanedRepos.empty() )
888 {
889 for ( const auto & repoInfo : orphanedRepos )
890 {
891 MIL << "Delete orphaned service repo " << repoInfo.alias() << endl;
892 // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
893 // %1% = service name
894 // %2% = repository name
895 JobReport::warning( str::Format(_("Unknown service '%1%': Removing orphaned service repository '%2%'"))
896 % repoInfo.service()
897 % repoInfo.alias() );
898 try {
899 removeRepository( repoInfo );
900 }
901 catch ( const Exception & caugth )
902 {
904 }
905 }
906 }
907
908 // bsc#1210740: Don't cleanup if read-only mode was promised.
909 if ( not zypp_readonly_hack::IGotIt() ) {
910 // delete metadata folders without corresponding repo (e.g. old tmp directories)
911 //
912 // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
913 // we'd need somemagic file to identify zypp cache directories. Without this
914 // we may easily remove user data (zypper --pkg-cache-dir . download ...)
915 repoEscAliases.sort();
916 cleanupNonRepoMetadtaFolders( _options.repoRawCachePath,
918 repoEscAliases );
919 cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath,
921 repoEscAliases );
922 // bsc#1204956: Tweak to prevent auto pruning package caches
923 if ( autoPruneInDir( _options.repoPackagesCachePath ) )
924 cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath,
926 repoEscAliases );
927 }
928 }
929 MIL << "end construct known repos" << endl;
930 }
931
933
934 RepoStatus RepoManager::Impl::metadataStatus( const RepoInfo & info ) const
935 {
936 Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
937 Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
938
939 RepoType repokind = info.type();
940 // If unknown, probe the local metadata
941 if ( repokind == RepoType::NONE )
942 repokind = probeCache( productdatapath );
943
944 // NOTE: The calling code expects an empty RepoStatus being returned
945 // if the metadata cache is empty. So additioanl components like the
946 // RepoInfos status are joined after the switch IFF the status is not
947 // empty.
948 RepoStatus status;
949 switch ( repokind.toEnum() )
950 {
951 case RepoType::RPMMD_e :
952 status = RepoStatus( productdatapath/"repodata/repomd.xml");
953 if ( info.requireStatusWithMediaFile() )
954 status = status && RepoStatus( mediarootpath/"media.1/media" );
955 break;
956
957 case RepoType::YAST2_e :
958 status = RepoStatus( productdatapath/"content" ) && RepoStatus( mediarootpath/"media.1/media" );
959 break;
960
962 status = RepoStatus::fromCookieFile( productdatapath/"cookie" ); // dir status at last refresh
963 break;
964
965 case RepoType::NONE_e :
966 // Return default RepoStatus in case of RepoType::NONE
967 // indicating it should be created?
968 // ZYPP_THROW(RepoUnknownTypeException());
969 break;
970 }
971
972 if ( ! status.empty() )
973 status = status && RepoStatus( info );
974
975 return status;
976 }
977
978
979 void RepoManager::Impl::touchIndexFile( const RepoInfo & info )
980 {
981 Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
982
983 RepoType repokind = info.type();
984 if ( repokind.toEnum() == RepoType::NONE_e )
985 // unknown, probe the local metadata
986 repokind = probeCache( productdatapath );
987 // if still unknown, just return
988 if (repokind == RepoType::NONE_e)
989 return;
990
991 Pathname p;
992 switch ( repokind.toEnum() )
993 {
994 case RepoType::RPMMD_e :
995 p = Pathname(productdatapath + "/repodata/repomd.xml");
996 break;
997
998 case RepoType::YAST2_e :
999 p = Pathname(productdatapath + "/content");
1000 break;
1001
1003 p = Pathname(productdatapath + "/cookie");
1004 break;
1005
1006 case RepoType::NONE_e :
1007 default:
1008 break;
1009 }
1010
1011 // touch the file, ignore error (they are logged anyway)
1013 }
1014
1015
1016 RepoManager::RefreshCheckStatus RepoManager::Impl::checkIfToRefreshMetadata( const RepoInfo & info, const Url & url, RawMetadataRefreshPolicy policy )
1017 {
1018 assert_alias(info);
1019 try
1020 {
1021 MIL << "Check if to refresh repo " << info.alias() << " at " << url << " (" << info.type() << ")" << endl;
1022
1023 refreshGeoIPData( { url } );
1024
1025 // first check old (cached) metadata
1026 Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
1027 filesystem::assert_dir( mediarootpath );
1028 RepoStatus oldstatus = metadataStatus( info );
1029
1030 if ( oldstatus.empty() )
1031 {
1032 MIL << "No cached metadata, going to refresh" << endl;
1033 return REFRESH_NEEDED;
1034 }
1035
1036 if ( url.schemeIsVolatile() )
1037 {
1038 MIL << "Never refresh CD/DVD" << endl;
1039 return REPO_UP_TO_DATE;
1040 }
1041
1042 if ( policy == RefreshForced )
1043 {
1044 MIL << "Forced refresh!" << endl;
1045 return REFRESH_NEEDED;
1046 }
1047
1048 if ( url.schemeIsLocal() )
1049 {
1050 policy = RefreshIfNeededIgnoreDelay;
1051 }
1052
1053 // Check whether repo.refresh.delay applies...
1054 if ( policy != RefreshIfNeededIgnoreDelay )
1055 {
1056 // bsc#1174016: Prerequisite to skipping the refresh is that metadata
1057 // and solv cache status match. They will not, if the repos URL was
1058 // changed e.g. due to changed repovars.
1059 RepoStatus cachestatus = cacheStatus( info );
1060
1061 if ( oldstatus == cachestatus )
1062 {
1063 // difference in seconds
1064 double diff = ::difftime( (Date::ValueType)Date::now(), (Date::ValueType)oldstatus.timestamp() ) / 60;
1065 if ( diff < ZConfig::instance().repo_refresh_delay() )
1066 {
1067 if ( diff < 0 )
1068 {
1069 WAR << "Repository '" << info.alias() << "' was refreshed in the future!" << endl;
1070 }
1071 else
1072 {
1073 MIL << "Repository '" << info.alias()
1074 << "' has been refreshed less than repo.refresh.delay ("
1076 << ") minutes ago. Advising to skip refresh" << endl;
1077 return REPO_CHECK_DELAYED;
1078 }
1079 }
1080 }
1081 else {
1082 MIL << "Metadata and solv cache don't match. Check data on server..." << endl;
1083 }
1084 }
1085
1086 repo::RepoType repokind = info.type();
1087 // if unknown: probe it
1088 if ( repokind == RepoType::NONE )
1089 repokind = probe( url, info.path() );
1090
1091 // retrieve newstatus
1092 RepoStatus newstatus;
1093 switch ( repokind.toEnum() )
1094 {
1095 case RepoType::RPMMD_e:
1096 {
1097 MediaSetAccess media( url );
1098 newstatus = RepoStatus( info ) && yum::Downloader( info, mediarootpath ).status( media );
1099 }
1100 break;
1101
1102 case RepoType::YAST2_e:
1103 {
1104 MediaSetAccess media( url );
1105 newstatus = RepoStatus( info ) && susetags::Downloader( info, mediarootpath ).status( media );
1106 }
1107 break;
1108
1110 newstatus = RepoStatus( info ) && RepoStatus( MediaMounter(url).getPathName(info.path()) ); // dir status
1111 break;
1112
1113 default:
1114 case RepoType::NONE_e:
1116 break;
1117 }
1118
1119 // check status
1120 if ( oldstatus == newstatus )
1121 {
1122 MIL << "repo has not changed" << endl;
1123 touchIndexFile( info );
1124 return REPO_UP_TO_DATE;
1125 }
1126 else // includes newstatus.empty() if e.g. repo format changed
1127 {
1128 MIL << "repo has changed, going to refresh" << endl;
1129 return REFRESH_NEEDED;
1130 }
1131 }
1132 catch ( const Exception &e )
1133 {
1134 ZYPP_CAUGHT(e);
1135 ERR << "refresh check failed for " << url << endl;
1136 ZYPP_RETHROW(e);
1137 }
1138
1139 return REFRESH_NEEDED; // default
1140 }
1141
1142
1143 void RepoManager::Impl::refreshMetadata( const RepoInfo & info, RawMetadataRefreshPolicy policy, const ProgressData::ReceiverFnc & progress )
1144 {
1145 assert_alias(info);
1146 assert_urls(info);
1147
1148 // make sure geoIP data is up 2 date
1149 refreshGeoIPData( info.baseUrls() );
1150
1151 // we will throw this later if no URL checks out fine
1152 RepoException rexception( info, PL_("Valid metadata not found at specified URL",
1153 "Valid metadata not found at specified URLs",
1154 info.baseUrlsSize() ) );
1155
1156 // Suppress (interactive) media::MediaChangeReport if we in have multiple basurls (>1)
1158 // try urls one by one
1159 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
1160 {
1161 try
1162 {
1163 Url url(*it);
1164
1165 // check whether to refresh metadata
1166 // if the check fails for this url, it throws, so another url will be checked
1167 if (checkIfToRefreshMetadata(info, url, policy)!=REFRESH_NEEDED)
1168 return;
1169
1170 MIL << "Going to refresh metadata from " << url << endl;
1171
1172 // bsc#1048315: Always re-probe in case of repo format change.
1173 // TODO: Would be sufficient to verify the type and re-probe
1174 // if verification failed (or type is RepoType::NONE)
1175 repo::RepoType repokind = info.type();
1176 {
1177 repo::RepoType probed = probe( *it, info.path() );
1178 if ( repokind != probed )
1179 {
1180 repokind = probed;
1181 // update probed type only for repos in system
1182 for_( it, repoBegin(), repoEnd() )
1183 {
1184 if ( info.alias() == (*it).alias() )
1185 {
1186 RepoInfo modifiedrepo = *it;
1187 modifiedrepo.setType( repokind );
1188 // don't modify .repo in refresh.
1189 // modifyRepository( info.alias(), modifiedrepo );
1190 break;
1191 }
1192 }
1193 // Adjust the probed type in RepoInfo
1194 info.setProbedType( repokind ); // lazy init!
1195 }
1196 // no need to continue with an unknown type
1197 if ( repokind.toEnum() == RepoType::NONE_e )
1199 }
1200
1201 Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
1202 if( filesystem::assert_dir(mediarootpath) )
1203 {
1204 Exception ex(str::form( _("Can't create %s"), mediarootpath.c_str()) );
1205 ZYPP_THROW(ex);
1206 }
1207
1208 // create temp dir as sibling of mediarootpath
1209 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) );
1210 if( tmpdir.path().empty() )
1211 {
1212 Exception ex(_("Can't create metadata cache directory."));
1213 ZYPP_THROW(ex);
1214 }
1215
1216 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
1217 ( repokind.toEnum() == RepoType::YAST2_e ) )
1218 {
1219 MediaSetAccess media(url);
1220 shared_ptr<repo::Downloader> downloader_ptr;
1221
1222 MIL << "Creating downloader for [ " << info.alias() << " ]" << endl;
1223
1224 if ( repokind.toEnum() == RepoType::RPMMD_e ) {
1225 downloader_ptr.reset(new yum::Downloader(info, mediarootpath ));
1226 if ( _pluginRepoverification.checkIfNeeded() )
1227 downloader_ptr->setPluginRepoverification( _pluginRepoverification ); // susetags is dead so we apply just to yum
1228 }
1229 else
1230 downloader_ptr.reset( new susetags::Downloader(info, mediarootpath) );
1231
1238 for_( it, repoBegin(), repoEnd() )
1239 {
1240 Pathname cachepath(rawcache_path_for_repoinfo( _options, *it ));
1241 if ( PathInfo(cachepath).isExist() )
1242 downloader_ptr->addCachePath(cachepath);
1243 }
1244
1245 downloader_ptr->download( media, tmpdir.path() );
1246 }
1247 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
1248 {
1249 // as substitute for real metadata remember the checksum of the directory we refreshed
1250 MediaMounter media( url );
1251 RepoStatus newstatus = RepoStatus( media.getPathName( info.path() ) ); // dir status
1252
1253 Pathname productpath( tmpdir.path() / info.path() );
1254 filesystem::assert_dir( productpath );
1255 newstatus.saveToCookieFile( productpath/"cookie" );
1256 }
1257 else
1258 {
1260 }
1261
1262 // ok we have the metadata, now exchange
1263 // the contents
1264 filesystem::exchange( tmpdir.path(), mediarootpath );
1265 if ( ! isTmpRepo( info ) )
1266 reposManip(); // remember to trigger appdata refresh
1267
1268 // we are done.
1269 return;
1270 }
1271 catch ( const Exception &e )
1272 {
1273 ZYPP_CAUGHT(e);
1274 ERR << "Trying another url..." << endl;
1275
1276 // remember the exception caught for the *first URL*
1277 // if all other URLs fail, the rexception will be thrown with the
1278 // cause of the problem of the first URL remembered
1279 if (it == info.baseUrlsBegin())
1280 rexception.remember(e);
1281 else
1282 rexception.addHistory( e.asUserString() );
1283
1284 }
1285 } // for every url
1286 ERR << "No more urls..." << endl;
1287 ZYPP_THROW(rexception);
1288 }
1289
1291
1292 void RepoManager::Impl::cleanMetadata( const RepoInfo & info, const ProgressData::ReceiverFnc & progressfnc )
1293 {
1294 ProgressData progress(100);
1295 progress.sendTo(progressfnc);
1296 filesystem::recursive_rmdir( ZConfig::instance().geoipCachePath() );
1297 filesystem::recursive_rmdir( rawcache_path_for_repoinfo(_options, info) );
1298 progress.toMax();
1299 }
1300
1301
1302 void RepoManager::Impl::cleanPackages( const RepoInfo & info, const ProgressData::ReceiverFnc & progressfnc, bool isAutoClean_r )
1303 {
1304 ProgressData progress(100);
1305 progress.sendTo(progressfnc);
1306
1307 // bsc#1204956: Tweak to prevent auto pruning package caches
1308 const Pathname & rpc { packagescache_path_for_repoinfo(_options, info) };
1309 if ( not isAutoClean_r || autoPruneInDir( rpc.dirname() ) )
1311 progress.toMax();
1312 }
1313
1314
1315 void RepoManager::Impl::buildCache( const RepoInfo & info, CacheBuildPolicy policy, const ProgressData::ReceiverFnc & progressrcv )
1316 {
1317 assert_alias(info);
1318 Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
1319 Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
1320
1321 if( filesystem::assert_dir(_options.repoCachePath) )
1322 {
1323 Exception ex(str::form( _("Can't create %s"), _options.repoCachePath.c_str()) );
1324 ZYPP_THROW(ex);
1325 }
1326 RepoStatus raw_metadata_status = metadataStatus(info);
1327 if ( raw_metadata_status.empty() )
1328 {
1329 /* if there is no cache at this point, we refresh the raw
1330 in case this is the first time - if it's !autorefresh,
1331 we may still refresh */
1332 refreshMetadata(info, RefreshIfNeeded, progressrcv );
1333 raw_metadata_status = metadataStatus(info);
1334 }
1335
1336 bool needs_cleaning = false;
1337 if ( isCached( info ) )
1338 {
1339 MIL << info.alias() << " is already cached." << endl;
1340 RepoStatus cache_status = cacheStatus(info);
1341
1342 if ( cache_status == raw_metadata_status )
1343 {
1344 MIL << info.alias() << " cache is up to date with metadata." << endl;
1345 if ( policy == BuildIfNeeded )
1346 {
1347 // On the fly add missing solv.idx files for bash completion.
1348 const Pathname & base = solv_path_for_repoinfo( _options, info);
1349 if ( ! PathInfo(base/"solv.idx").isExist() )
1350 sat::updateSolvFileIndex( base/"solv" );
1351
1352 return;
1353 }
1354 else {
1355 MIL << info.alias() << " cache rebuild is forced" << endl;
1356 }
1357 }
1358
1359 needs_cleaning = true;
1360 }
1361
1362 ProgressData progress(100);
1364 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1365 progress.name(str::form(_("Building repository '%s' cache"), info.label().c_str()));
1366 progress.toMin();
1367
1368 if (needs_cleaning)
1369 {
1370 cleanCache(info);
1371 }
1372
1373 MIL << info.alias() << " building cache..." << info.type() << endl;
1374
1375 Pathname base = solv_path_for_repoinfo( _options, info);
1376
1377 if( filesystem::assert_dir(base) )
1378 {
1379 Exception ex(str::form( _("Can't create %s"), base.c_str()) );
1380 ZYPP_THROW(ex);
1381 }
1382
1383 if( ! PathInfo(base).userMayW() )
1384 {
1385 Exception ex(str::form( _("Can't create cache at %s - no writing permissions."), base.c_str()) );
1386 ZYPP_THROW(ex);
1387 }
1388 Pathname solvfile = base / "solv";
1389
1390 // do we have type?
1391 repo::RepoType repokind = info.type();
1392
1393 // if the type is unknown, try probing.
1394 switch ( repokind.toEnum() )
1395 {
1396 case RepoType::NONE_e:
1397 // unknown, probe the local metadata
1398 repokind = probeCache( productdatapath );
1399 break;
1400 default:
1401 break;
1402 }
1403
1404 MIL << "repo type is " << repokind << endl;
1405
1406 switch ( repokind.toEnum() )
1407 {
1408 case RepoType::RPMMD_e :
1409 case RepoType::YAST2_e :
1411 {
1412 // Take care we unlink the solvfile on exception
1413 ManagedFile guard( solvfile, filesystem::unlink );
1414 scoped_ptr<MediaMounter> forPlainDirs;
1415
1417 cmd.push_back( PathInfo( "/usr/bin/repo2solv" ).isFile() ? "repo2solv" : "repo2solv.sh" );
1418 // repo2solv expects -o as 1st arg!
1419 cmd.push_back( "-o" );
1420 cmd.push_back( solvfile.asString() );
1421 cmd.push_back( "-X" ); // autogenerate pattern from pattern-package
1422 // bsc#1104415: no more application support // cmd.push_back( "-A" ); // autogenerate application pseudo packages
1423
1424 if ( repokind == RepoType::RPMPLAINDIR )
1425 {
1426 forPlainDirs.reset( new MediaMounter( info.url() ) );
1427 // recusive for plaindir as 2nd arg!
1428 cmd.push_back( "-R" );
1429 // FIXME this does only work form dir: URLs
1430 cmd.push_back( forPlainDirs->getPathName( info.path() ).c_str() );
1431 }
1432 else
1433 cmd.push_back( productdatapath.asString() );
1434
1436 std::string errdetail;
1437
1438 for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
1439 WAR << " " << output;
1440 errdetail += output;
1441 }
1442
1443 int ret = prog.close();
1444 if ( ret != 0 )
1445 {
1446 RepoException ex(info, str::form( _("Failed to cache repo (%d)."), ret ));
1447 ex.addHistory( str::Str() << prog.command() << endl << errdetail << prog.execError() ); // errdetail lines are NL-terminaled!
1448 ZYPP_THROW(ex);
1449 }
1450
1451 // We keep it.
1452 guard.resetDispose();
1453 sat::updateSolvFileIndex( solvfile ); // content digest for zypper bash completion
1454 }
1455 break;
1456 default:
1457 ZYPP_THROW(RepoUnknownTypeException( info, _("Unhandled repository type") ));
1458 break;
1459 }
1460 // update timestamp and checksum
1461 setCacheStatus(info, raw_metadata_status);
1462 MIL << "Commit cache.." << endl;
1463 progress.toMax();
1464 }
1465
1467
1468
1475 repo::RepoType RepoManager::Impl::probe( const Url & url, const Pathname & path ) const
1476 {
1477 MIL << "going to probe the repo type at " << url << " (" << path << ")" << endl;
1478
1479 if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName()/path ).isDir() )
1480 {
1481 // Handle non existing local directory in advance, as
1482 // MediaSetAccess does not support it.
1483 MIL << "Probed type NONE (not exists) at " << url << " (" << path << ")" << endl;
1484 return repo::RepoType::NONE;
1485 }
1486
1487 // prepare exception to be thrown if the type could not be determined
1488 // due to a media exception. We can't throw right away, because of some
1489 // problems with proxy servers returning an incorrect error
1490 // on ftp file-not-found(bnc #335906). Instead we'll check another types
1491 // before throwing.
1492
1493 // TranslatorExplanation '%s' is an URL
1494 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
1495 bool gotMediaException = false;
1496 try
1497 {
1498 MediaSetAccess access(url);
1499 try
1500 {
1501 if ( access.doesFileExist(path/"/repodata/repomd.xml") )
1502 {
1503 MIL << "Probed type RPMMD at " << url << " (" << path << ")" << endl;
1504 return repo::RepoType::RPMMD;
1505 }
1506 }
1507 catch ( const media::MediaException &e )
1508 {
1509 ZYPP_CAUGHT(e);
1510 DBG << "problem checking for repodata/repomd.xml file" << endl;
1511 enew.remember(e);
1512 gotMediaException = true;
1513 }
1514
1515 try
1516 {
1517 if ( access.doesFileExist(path/"/content") )
1518 {
1519 MIL << "Probed type YAST2 at " << url << " (" << path << ")" << endl;
1520 return repo::RepoType::YAST2;
1521 }
1522 }
1523 catch ( const media::MediaException &e )
1524 {
1525 ZYPP_CAUGHT(e);
1526 DBG << "problem checking for content file" << endl;
1527 enew.remember(e);
1528 gotMediaException = true;
1529 }
1530
1531 // if it is a non-downloading URL denoting a directory (bsc#1191286: and no plugin)
1532 if ( ! ( url.schemeIsDownloading() || url.schemeIsPlugin() ) )
1533 {
1534 MediaMounter media( url );
1535 if ( PathInfo(media.getPathName()/path).isDir() )
1536 {
1537 // allow empty dirs for now
1538 MIL << "Probed type RPMPLAINDIR at " << url << " (" << path << ")" << endl;
1540 }
1541 }
1542 }
1543 catch ( const Exception &e )
1544 {
1545 ZYPP_CAUGHT(e);
1546 // TranslatorExplanation '%s' is an URL
1547 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
1548 enew.remember(e);
1549 ZYPP_THROW(enew);
1550 }
1551
1552 if (gotMediaException)
1553 ZYPP_THROW(enew);
1554
1555 MIL << "Probed type NONE at " << url << " (" << path << ")" << endl;
1556 return repo::RepoType::NONE;
1557 }
1558
1564 repo::RepoType RepoManager::Impl::probeCache( const Pathname & path_r ) const
1565 {
1566 MIL << "going to probe the cached repo at " << path_r << endl;
1567
1569
1570 if ( PathInfo(path_r/"/repodata/repomd.xml").isFile() )
1571 { ret = repo::RepoType::RPMMD; }
1572 else if ( PathInfo(path_r/"/content").isFile() )
1573 { ret = repo::RepoType::YAST2; }
1574 else if ( PathInfo(path_r).isDir() )
1576
1577 MIL << "Probed cached type " << ret << " at " << path_r << endl;
1578 return ret;
1579 }
1580
1582
1583 void RepoManager::Impl::cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv )
1584 {
1585 MIL << "Going to clean up garbage in cache dirs" << endl;
1586
1587 ProgressData progress(300);
1588 progress.sendTo(progressrcv);
1589 progress.toMin();
1590
1591 std::list<Pathname> cachedirs;
1592 cachedirs.push_back(_options.repoRawCachePath);
1593 cachedirs.push_back(_options.repoPackagesCachePath);
1594 cachedirs.push_back(_options.repoSolvCachePath);
1595
1596 for_( dir, cachedirs.begin(), cachedirs.end() )
1597 {
1598 if ( PathInfo(*dir).isExist() )
1599 {
1600 std::list<Pathname> entries;
1601 if ( filesystem::readdir( entries, *dir, false ) != 0 )
1602 // TranslatorExplanation '%s' is a pathname
1603 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir->c_str())));
1604
1605 unsigned sdircount = entries.size();
1606 unsigned sdircurrent = 1;
1607 for_( subdir, entries.begin(), entries.end() )
1608 {
1609 // if it does not belong known repo, make it disappear
1610 bool found = false;
1611 for_( r, repoBegin(), repoEnd() )
1612 if ( subdir->basename() == r->escaped_alias() )
1613 { found = true; break; }
1614
1615 if ( ! found && ( Date::now()-PathInfo(*subdir).mtime() > Date::day ) )
1616 filesystem::recursive_rmdir( *subdir );
1617
1618 progress.set( progress.val() + sdircurrent * 100 / sdircount );
1619 ++sdircurrent;
1620 }
1621 }
1622 else
1623 progress.set( progress.val() + 100 );
1624 }
1625 progress.toMax();
1626 }
1627
1629
1630 void RepoManager::Impl::cleanCache( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
1631 {
1632 ProgressData progress(100);
1633 progress.sendTo(progressrcv);
1634 progress.toMin();
1635
1636 MIL << "Removing raw metadata cache for " << info.alias() << endl;
1637 filesystem::recursive_rmdir(solv_path_for_repoinfo(_options, info));
1638
1639 progress.toMax();
1640 }
1641
1643
1644 void RepoManager::Impl::loadFromCache( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
1645 {
1646 assert_alias(info);
1647 Pathname solvfile = solv_path_for_repoinfo(_options, info) / "solv";
1648
1649 if ( ! PathInfo(solvfile).isExist() )
1651
1653 try
1654 {
1655 Repository repo = sat::Pool::instance().addRepoSolv( solvfile, info );
1656 // test toolversion in order to rebuild solv file in case
1657 // it was written by a different libsolv-tool parser.
1658 const std::string & toolversion( sat::LookupRepoAttr( sat::SolvAttr::repositoryToolVersion, repo ).begin().asString() );
1659 if ( toolversion != LIBSOLV_TOOLVERSION )
1660 {
1661 repo.eraseFromPool();
1662 ZYPP_THROW(Exception(str::Str() << "Solv-file was created by '"<<toolversion<<"'-parser (want "<<LIBSOLV_TOOLVERSION<<")."));
1663 }
1664 }
1665 catch ( const Exception & exp )
1666 {
1667 ZYPP_CAUGHT( exp );
1668 MIL << "Try to handle exception by rebuilding the solv-file" << endl;
1669 cleanCache( info, progressrcv );
1670 buildCache( info, BuildIfNeeded, progressrcv );
1671
1672 sat::Pool::instance().addRepoSolv( solvfile, info );
1673 }
1674 }
1675
1677
1678 void RepoManager::Impl::addRepository( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
1679 {
1680 assert_alias(info);
1681
1682 ProgressData progress(100);
1684 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1685 progress.name(str::form(_("Adding repository '%s'"), info.label().c_str()));
1686 progress.toMin();
1687
1688 MIL << "Try adding repo " << info << endl;
1689
1690 RepoInfo tosave = info;
1691 if ( repos().find(tosave) != repos().end() )
1693
1694 // check the first url for now
1695 if ( _options.probe )
1696 {
1697 DBG << "unknown repository type, probing" << endl;
1698 assert_urls(tosave);
1699
1700 RepoType probedtype( probe( tosave.url(), info.path() ) );
1701 if ( probedtype == RepoType::NONE )
1703 else
1704 tosave.setType(probedtype);
1705 }
1706
1707 progress.set(50);
1708
1709 // assert the directory exists
1710 filesystem::assert_dir(_options.knownReposPath);
1711
1712 Pathname repofile = generateNonExistingName(
1713 _options.knownReposPath, generateFilename(tosave));
1714 // now we have a filename that does not exists
1715 MIL << "Saving repo in " << repofile << endl;
1716
1717 std::ofstream file(repofile.c_str());
1718 if (!file)
1719 {
1720 // TranslatorExplanation '%s' is a filename
1721 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
1722 }
1723
1724 tosave.dumpAsIniOn(file);
1725 tosave.setFilepath(repofile);
1726 tosave.setMetadataPath( rawcache_path_for_repoinfo( _options, tosave ) );
1727 tosave.setPackagesPath( packagescache_path_for_repoinfo( _options, tosave ) );
1728 {
1729 // We should fix the API as we must inject those paths
1730 // into the repoinfo in order to keep it usable.
1731 RepoInfo & oinfo( const_cast<RepoInfo &>(info) );
1732 oinfo.setFilepath(repofile);
1733 oinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, tosave ) );
1734 oinfo.setPackagesPath( packagescache_path_for_repoinfo( _options, tosave ) );
1735 }
1736 reposManip().insert(tosave);
1737
1738 progress.set(90);
1739
1740 // check for credentials in Urls
1741 UrlCredentialExtractor( _options.rootDir ).collect( tosave.baseUrls() );
1742
1743 HistoryLog(_options.rootDir).addRepository(tosave);
1744
1745 progress.toMax();
1746 MIL << "done" << endl;
1747 }
1748
1749
1750 void RepoManager::Impl::addRepositories( const Url & url, const ProgressData::ReceiverFnc & progressrcv )
1751 {
1752 std::list<RepoInfo> repos = readRepoFile(url);
1753 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1754 it != repos.end();
1755 ++it )
1756 {
1757 // look if the alias is in the known repos.
1758 for_ ( kit, repoBegin(), repoEnd() )
1759 {
1760 if ( (*it).alias() == (*kit).alias() )
1761 {
1762 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
1764 }
1765 }
1766 }
1767
1768 std::string filename = Pathname(url.getPathName()).basename();
1769
1770 if ( filename == Pathname() )
1771 {
1772 // TranslatorExplanation '%s' is an URL
1773 ZYPP_THROW(RepoException(str::form( _("Invalid repo file name at '%s'"), url.asString().c_str() )));
1774 }
1775
1776 // assert the directory exists
1777 filesystem::assert_dir(_options.knownReposPath);
1778
1779 Pathname repofile = generateNonExistingName(_options.knownReposPath, filename);
1780 // now we have a filename that does not exists
1781 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
1782
1783 std::ofstream file(repofile.c_str());
1784 if (!file)
1785 {
1786 // TranslatorExplanation '%s' is a filename
1787 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
1788 }
1789
1790 for ( std::list<RepoInfo>::iterator it = repos.begin();
1791 it != repos.end();
1792 ++it )
1793 {
1794 MIL << "Saving " << (*it).alias() << endl;
1795 it->dumpAsIniOn(file);
1796 it->setFilepath(repofile);
1797 it->setMetadataPath( rawcache_path_for_repoinfo( _options, *it ) );
1798 it->setPackagesPath( packagescache_path_for_repoinfo( _options, *it ) );
1799 reposManip().insert(*it);
1800
1801 HistoryLog(_options.rootDir).addRepository(*it);
1802 }
1803
1804 MIL << "done" << endl;
1805 }
1806
1808
1809 void RepoManager::Impl::removeRepository( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
1810 {
1811 ProgressData progress;
1813 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1814 progress.name(str::form(_("Removing repository '%s'"), info.label().c_str()));
1815
1816 MIL << "Going to delete repo " << info.alias() << endl;
1817
1818 for_( it, repoBegin(), repoEnd() )
1819 {
1820 // they can be the same only if the provided is empty, that means
1821 // the provided repo has no alias
1822 // then skip
1823 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
1824 continue;
1825
1826 // TODO match by url
1827
1828 // we have a matcing repository, now we need to know
1829 // where it does come from.
1830 RepoInfo todelete = *it;
1831 if (todelete.filepath().empty())
1832 {
1833 ZYPP_THROW(RepoException( todelete, _("Can't figure out where the repo is stored.") ));
1834 }
1835 else
1836 {
1837 // figure how many repos are there in the file:
1838 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
1839 if ( filerepos.size() == 0 // bsc#984494: file may have already been deleted
1840 ||(filerepos.size() == 1 && filerepos.front().alias() == todelete.alias() ) )
1841 {
1842 // easy: file does not exist, contains no or only the repo to delete: delete the file
1843 int ret = filesystem::unlink( todelete.filepath() );
1844 if ( ! ( ret == 0 || ret == ENOENT ) )
1845 {
1846 // TranslatorExplanation '%s' is a filename
1847 ZYPP_THROW(RepoException( todelete, str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
1848 }
1849 MIL << todelete.alias() << " successfully deleted." << endl;
1850 }
1851 else
1852 {
1853 // there are more repos in the same file
1854 // write them back except the deleted one.
1855 //TmpFile tmp;
1856 //std::ofstream file(tmp.path().c_str());
1857
1858 // assert the directory exists
1860
1861 std::ofstream file(todelete.filepath().c_str());
1862 if (!file)
1863 {
1864 // TranslatorExplanation '%s' is a filename
1865 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
1866 }
1867 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1868 fit != filerepos.end();
1869 ++fit )
1870 {
1871 if ( (*fit).alias() != todelete.alias() )
1872 (*fit).dumpAsIniOn(file);
1873 }
1874 }
1875
1876 CombinedProgressData cSubprogrcv(progress, 20);
1877 CombinedProgressData mSubprogrcv(progress, 40);
1878 CombinedProgressData pSubprogrcv(progress, 40);
1879 // now delete it from cache
1880 if ( isCached(todelete) )
1881 cleanCache( todelete, cSubprogrcv);
1882 // now delete metadata (#301037)
1883 cleanMetadata( todelete, mSubprogrcv );
1884 cleanPackages( todelete, pSubprogrcv, true/*isAutoClean*/ );
1885 reposManip().erase(todelete);
1886 MIL << todelete.alias() << " successfully deleted." << endl;
1887 HistoryLog(_options.rootDir).removeRepository(todelete);
1888 return;
1889 } // else filepath is empty
1890
1891 }
1892 // should not be reached on a sucess workflow
1894 }
1895
1897
1898 void RepoManager::Impl::modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, const ProgressData::ReceiverFnc & progressrcv )
1899 {
1900 RepoInfo toedit = getRepositoryInfo(alias);
1901 RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data
1902
1903 // check if the new alias already exists when renaming the repo
1904 if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
1905 {
1907 }
1908
1909 if (toedit.filepath().empty())
1910 {
1911 ZYPP_THROW(RepoException( toedit, _("Can't figure out where the repo is stored.") ));
1912 }
1913 else
1914 {
1915 // figure how many repos are there in the file:
1916 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
1917
1918 // there are more repos in the same file
1919 // write them back except the deleted one.
1920 //TmpFile tmp;
1921 //std::ofstream file(tmp.path().c_str());
1922
1923 // assert the directory exists
1925
1926 std::ofstream file(toedit.filepath().c_str());
1927 if (!file)
1928 {
1929 // TranslatorExplanation '%s' is a filename
1930 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
1931 }
1932 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1933 fit != filerepos.end();
1934 ++fit )
1935 {
1936 // if the alias is different, dump the original
1937 // if it is the same, dump the provided one
1938 if ( (*fit).alias() != toedit.alias() )
1939 (*fit).dumpAsIniOn(file);
1940 else
1941 newinfo.dumpAsIniOn(file);
1942 }
1943
1944 if ( toedit.enabled() && !newinfo.enabled() )
1945 {
1946 // On the fly remove solv.idx files for bash completion if a repo gets disabled.
1947 const Pathname & solvidx = solv_path_for_repoinfo(_options, newinfo)/"solv.idx";
1948 if ( PathInfo(solvidx).isExist() )
1949 filesystem::unlink( solvidx );
1950 }
1951
1952 newinfo.setFilepath(toedit.filepath());
1953 newinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, newinfo ) );
1954 newinfo.setPackagesPath( packagescache_path_for_repoinfo( _options, newinfo ) );
1955 {
1956 // We should fix the API as we must inject those paths
1957 // into the repoinfo in order to keep it usable.
1958 RepoInfo & oinfo( const_cast<RepoInfo &>(newinfo_r) );
1959 oinfo.setFilepath(toedit.filepath());
1960 oinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, newinfo ) );
1961 oinfo.setPackagesPath( packagescache_path_for_repoinfo( _options, newinfo ) );
1962 }
1963 reposManip().erase(toedit);
1964 reposManip().insert(newinfo);
1965 // check for credentials in Urls
1966 UrlCredentialExtractor( _options.rootDir ).collect( newinfo.baseUrls() );
1967 HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
1968 MIL << "repo " << alias << " modified" << endl;
1969 }
1970 }
1971
1973
1974 RepoInfo RepoManager::Impl::getRepositoryInfo( const std::string & alias, const ProgressData::ReceiverFnc & progressrcv )
1975 {
1976 RepoConstIterator it( findAlias( alias, repos() ) );
1977 if ( it != repos().end() )
1978 return *it;
1979 RepoInfo info;
1980 info.setAlias( alias );
1982 }
1983
1984
1985 RepoInfo RepoManager::Impl::getRepositoryInfo( const Url & url, const url::ViewOption & urlview, const ProgressData::ReceiverFnc & progressrcv )
1986 {
1987 for_( it, repoBegin(), repoEnd() )
1988 {
1989 for_( urlit, (*it).baseUrlsBegin(), (*it).baseUrlsEnd() )
1990 {
1991 if ( (*urlit).asString(urlview) == url.asString(urlview) )
1992 return *it;
1993 }
1994 }
1995 RepoInfo info;
1996 info.setBaseUrl( url );
1998 }
1999
2001 //
2002 // Services
2003 //
2005
2006 void RepoManager::Impl::addService( const ServiceInfo & service )
2007 {
2008 assert_alias( service );
2009
2010 // check if service already exists
2011 if ( hasService( service.alias() ) )
2013
2014 // Writable ServiceInfo is needed to save the location
2015 // of the .service file. Finaly insert into the service list.
2016 ServiceInfo toSave( service );
2017 saveService( toSave );
2018 _services.insert( toSave );
2019
2020 // check for credentials in Url
2021 UrlCredentialExtractor( _options.rootDir ).collect( toSave.url() );
2022
2023 MIL << "added service " << toSave.alias() << endl;
2024 }
2025
2027
2028 void RepoManager::Impl::removeService( const std::string & alias )
2029 {
2030 MIL << "Going to delete service " << alias << endl;
2031
2032 const ServiceInfo & service = getService( alias );
2033
2034 Pathname location = service.filepath();
2035 if( location.empty() )
2036 {
2037 ZYPP_THROW(ServiceException( service, _("Can't figure out where the service is stored.") ));
2038 }
2039
2040 ServiceSet tmpSet;
2041 parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
2042
2043 // only one service definition in the file
2044 if ( tmpSet.size() == 1 )
2045 {
2046 if ( filesystem::unlink(location) != 0 )
2047 {
2048 // TranslatorExplanation '%s' is a filename
2049 ZYPP_THROW(ServiceException( service, str::form( _("Can't delete '%s'"), location.c_str() ) ));
2050 }
2051 MIL << alias << " successfully deleted." << endl;
2052 }
2053 else
2054 {
2055 filesystem::assert_dir(location.dirname());
2056
2057 std::ofstream file(location.c_str());
2058 if( !file )
2059 {
2060 // TranslatorExplanation '%s' is a filename
2061 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), location.c_str() )));
2062 }
2063
2064 for_(it, tmpSet.begin(), tmpSet.end())
2065 {
2066 if( it->alias() != alias )
2067 it->dumpAsIniOn(file);
2068 }
2069
2070 MIL << alias << " successfully deleted from file " << location << endl;
2071 }
2072
2073 // now remove all repositories added by this service
2074 RepoCollector rcollector;
2075 getRepositoriesInService( alias,
2076 boost::make_function_output_iterator( bind( &RepoCollector::collect, &rcollector, _1 ) ) );
2077 // cannot do this directly in getRepositoriesInService - would invalidate iterators
2078 for_(rit, rcollector.repos.begin(), rcollector.repos.end())
2079 removeRepository(*rit);
2080 }
2081
2083
2084 void RepoManager::Impl::refreshServices( const RefreshServiceOptions & options_r )
2085 {
2086 // copy the set of services since refreshService
2087 // can eventually invalidate the iterator
2088 ServiceSet services( serviceBegin(), serviceEnd() );
2089 for_( it, services.begin(), services.end() )
2090 {
2091 if ( !it->enabled() )
2092 continue;
2093
2094 try {
2095 refreshService(*it, options_r);
2096 }
2097 catch ( const repo::ServicePluginInformalException & e )
2098 { ;/* ignore ServicePluginInformalException */ }
2099 }
2100 }
2101
2102 void RepoManager::Impl::refreshService( const std::string & alias, const RefreshServiceOptions & options_r )
2103 {
2104 ServiceInfo service( getService( alias ) );
2105 assert_alias( service );
2106 assert_url( service );
2107 MIL << "Going to refresh service '" << service.alias() << "', url: " << service.url() << ", opts: " << options_r << endl;
2108
2109 if ( service.ttl() && !( options_r.testFlag( RefreshService_forceRefresh) || options_r.testFlag( RefreshService_restoreStatus ) ) )
2110 {
2111 // Service defines a TTL; maybe we can re-use existing data without refresh.
2112 Date lrf = service.lrf();
2113 if ( lrf )
2114 {
2115 Date now( Date::now() );
2116 if ( lrf <= now )
2117 {
2118 if ( (lrf+=service.ttl()) > now ) // lrf+= !
2119 {
2120 MIL << "Skip: '" << service.alias() << "' metadata valid until " << lrf << endl;
2121 return;
2122 }
2123 }
2124 else
2125 WAR << "Force: '" << service.alias() << "' metadata last refresh in the future: " << lrf << endl;
2126 }
2127 }
2128
2129 // NOTE: It might be necessary to modify and rewrite the service info.
2130 // Either when probing the type, or when adjusting the repositories
2131 // enable/disable state.:
2132 bool serviceModified = false;
2133
2135
2136 // if the type is unknown, try probing.
2137 if ( service.type() == repo::ServiceType::NONE )
2138 {
2139 repo::ServiceType type = probeService( service.url() );
2140 if ( type != ServiceType::NONE )
2141 {
2142 service.setProbedType( type ); // lazy init!
2143 serviceModified = true;
2144 }
2145 }
2146
2147 // get target distro identifier
2148 std::string servicesTargetDistro = _options.servicesTargetDistro;
2149 if ( servicesTargetDistro.empty() )
2150 {
2151 servicesTargetDistro = Target::targetDistribution( Pathname() );
2152 }
2153 DBG << "ServicesTargetDistro: " << servicesTargetDistro << endl;
2154
2155 // parse it
2156 Date::Duration origTtl = service.ttl(); // FIXME Ugly hack: const service.ttl modified when parsing
2157 RepoCollector collector(servicesTargetDistro);
2158 // FIXME Ugly hack: ServiceRepos may throw ServicePluginInformalException
2159 // which is actually a notification. Using an exception for this
2160 // instead of signal/callback is bad. Needs to be fixed here, in refreshServices()
2161 // and in zypper.
2162 std::pair<DefaultIntegral<bool,false>, repo::ServicePluginInformalException> uglyHack;
2163 try {
2164 // FIXME bsc#1080693: Shortcoming of (plugin)services (and repos as well) is that they
2165 // are not aware of the RepoManagers rootDir. The service url, as created in known_services,
2166 // contains the full path to the script. The script however has to be executed chrooted.
2167 // Repos would need to know the RepoMangers rootDir to use the correct vars.d to replace
2168 // repos variables. Until RepoInfoBase is aware if the rootDir, we need to explicitly pass it
2169 // to ServiceRepos.
2170 ServiceRepos( _options.rootDir, service, bind( &RepoCollector::collect, &collector, _1 ) );
2171 }
2172 catch ( const repo::ServicePluginInformalException & e )
2173 {
2174 /* ignore ServicePluginInformalException and throw later */
2175 uglyHack.first = true;
2176 uglyHack.second = e;
2177 }
2178 if ( service.ttl() != origTtl ) // repoindex.xml changed ttl
2179 {
2180 if ( !service.ttl() )
2181 service.setLrf( Date() ); // don't need lrf when zero ttl
2182 serviceModified = true;
2183 }
2185 // On the fly remember the new repo states as defined the reopoindex.xml.
2186 // Move into ServiceInfo later.
2187 ServiceInfo::RepoStates newRepoStates;
2188
2189 // set service alias and base url for all collected repositories
2190 for_( it, collector.repos.begin(), collector.repos.end() )
2191 {
2192 // First of all: Prepend service alias:
2193 it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
2194 // set reference to the parent service
2195 it->setService( service.alias() );
2196
2197 // remember the new parsed repo state
2198 newRepoStates[it->alias()] = *it;
2199
2200 // - If the repo url was not set by the repoindex parser, set service's url.
2201 // - Libzypp currently has problem with separate url + path handling so just
2202 // append a path, if set, to the baseurls
2203 // - Credentials in the url authority will be extracted later, either if the
2204 // repository is added or if we check for changed urls.
2205 Pathname path;
2206 if ( !it->path().empty() )
2207 {
2208 if ( it->path() != "/" )
2209 path = it->path();
2210 it->setPath("");
2211 }
2212
2213 if ( it->baseUrlsEmpty() )
2214 {
2215 Url url( service.rawUrl() );
2216 if ( !path.empty() )
2217 url.setPathName( url.getPathName() / path );
2218 it->setBaseUrl( std::move(url) );
2219 }
2220 else if ( !path.empty() )
2221 {
2222 RepoInfo::url_set urls( it->rawBaseUrls() );
2223 for ( Url & url : urls )
2224 {
2225 url.setPathName( url.getPathName() / path );
2226 }
2227 it->setBaseUrls( std::move(urls) );
2228 }
2229 }
2230
2232 // Now compare collected repos with the ones in the system...
2233 //
2234 RepoInfoList oldRepos;
2235 getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) );
2236
2238 // find old repositories to remove...
2239 for_( oldRepo, oldRepos.begin(), oldRepos.end() )
2240 {
2241 if ( ! foundAliasIn( oldRepo->alias(), collector.repos ) )
2242 {
2243 if ( oldRepo->enabled() )
2244 {
2245 // Currently enabled. If this was a user modification remember the state.
2246 const auto & last = service.repoStates().find( oldRepo->alias() );
2247 if ( last != service.repoStates().end() && ! last->second.enabled )
2248 {
2249 DBG << "Service removes user enabled repo " << oldRepo->alias() << endl;
2250 service.addRepoToEnable( oldRepo->alias() );
2251 serviceModified = true;
2252 }
2253 else
2254 DBG << "Service removes enabled repo " << oldRepo->alias() << endl;
2255 }
2256 else
2257 DBG << "Service removes disabled repo " << oldRepo->alias() << endl;
2258
2259 removeRepository( *oldRepo );
2260 }
2261 }
2262
2264 // create missing repositories and modify existing ones if needed...
2265 UrlCredentialExtractor urlCredentialExtractor( _options.rootDir ); // To collect any credentials stored in repo URLs
2266 for_( it, collector.repos.begin(), collector.repos.end() )
2267 {
2268 // User explicitly requested the repo being enabled?
2269 // User explicitly requested the repo being disabled?
2270 // And hopefully not both ;) If so, enable wins.
2271
2272 TriBool toBeEnabled( indeterminate ); // indeterminate - follow the service request
2273 DBG << "Service request to " << (it->enabled()?"enable":"disable") << " service repo " << it->alias() << endl;
2274
2275 if ( options_r.testFlag( RefreshService_restoreStatus ) )
2276 {
2277 DBG << "Opt RefreshService_restoreStatus " << it->alias() << endl;
2278 // this overrides any pending request!
2279 // Remove from enable request list.
2280 // NOTE: repoToDisable is handled differently.
2281 // It gets cleared on each refresh.
2282 service.delRepoToEnable( it->alias() );
2283 // toBeEnabled stays indeterminate!
2284 }
2285 else
2286 {
2287 if ( service.repoToEnableFind( it->alias() ) )
2288 {
2289 DBG << "User request to enable service repo " << it->alias() << endl;
2290 toBeEnabled = true;
2291 // Remove from enable request list.
2292 // NOTE: repoToDisable is handled differently.
2293 // It gets cleared on each refresh.
2294 service.delRepoToEnable( it->alias() );
2295 serviceModified = true;
2296 }
2297 else if ( service.repoToDisableFind( it->alias() ) )
2298 {
2299 DBG << "User request to disable service repo " << it->alias() << endl;
2300 toBeEnabled = false;
2301 }
2302 }
2303
2304 RepoInfoList::iterator oldRepo( findAlias( it->alias(), oldRepos ) );
2305 if ( oldRepo == oldRepos.end() )
2306 {
2307 // Not found in oldRepos ==> a new repo to add
2308
2309 // Make sure the service repo is created with the appropriate enablement
2310 if ( ! indeterminate(toBeEnabled) )
2311 it->setEnabled( ( bool ) toBeEnabled );
2312
2313 DBG << "Service adds repo " << it->alias() << " " << (it->enabled()?"enabled":"disabled") << endl;
2314 addRepository( *it );
2315 }
2316 else
2317 {
2318 // ==> an exising repo to check
2319 bool oldRepoModified = false;
2320
2321 if ( indeterminate(toBeEnabled) )
2322 {
2323 // No user request: check for an old user modificaton otherwise follow service request.
2324 // NOTE: Assert toBeEnabled is boolean afterwards!
2325 if ( oldRepo->enabled() == it->enabled() )
2326 toBeEnabled = it->enabled(); // service requests no change to the system
2327 else if (options_r.testFlag( RefreshService_restoreStatus ) )
2328 {
2329 toBeEnabled = it->enabled(); // RefreshService_restoreStatus forced
2330 DBG << "Opt RefreshService_restoreStatus " << it->alias() << " forces " << (toBeEnabled?"enabled":"disabled") << endl;
2331 }
2332 else
2333 {
2334 const auto & last = service.repoStates().find( oldRepo->alias() );
2335 if ( last == service.repoStates().end() || last->second.enabled != it->enabled() )
2336 toBeEnabled = it->enabled(); // service request has changed since last refresh -> follow
2337 else
2338 {
2339 toBeEnabled = oldRepo->enabled(); // service request unchaned since last refresh -> keep user modification
2340 DBG << "User modified service repo " << it->alias() << " may stay " << (toBeEnabled?"enabled":"disabled") << endl;
2341 }
2342 }
2343 }
2344
2345 // changed enable?
2346 if ( toBeEnabled == oldRepo->enabled() )
2347 {
2348 DBG << "Service repo " << it->alias() << " stays " << (oldRepo->enabled()?"enabled":"disabled") << endl;
2349 }
2350 else if ( toBeEnabled )
2351 {
2352 DBG << "Service repo " << it->alias() << " gets enabled" << endl;
2353 oldRepo->setEnabled( true );
2354 oldRepoModified = true;
2355 }
2356 else
2357 {
2358 DBG << "Service repo " << it->alias() << " gets disabled" << endl;
2359 oldRepo->setEnabled( false );
2360 oldRepoModified = true;
2361 }
2362
2363 // all other attributes follow the service request:
2364
2365 // changed name (raw!)
2366 if ( oldRepo->rawName() != it->rawName() )
2367 {
2368 DBG << "Service repo " << it->alias() << " gets new NAME " << it->rawName() << endl;
2369 oldRepo->setName( it->rawName() );
2370 oldRepoModified = true;
2371 }
2372
2373 // changed autorefresh
2374 if ( oldRepo->autorefresh() != it->autorefresh() )
2375 {
2376 DBG << "Service repo " << it->alias() << " gets new AUTOREFRESH " << it->autorefresh() << endl;
2377 oldRepo->setAutorefresh( it->autorefresh() );
2378 oldRepoModified = true;
2379 }
2380
2381 // changed priority?
2382 if ( oldRepo->priority() != it->priority() )
2383 {
2384 DBG << "Service repo " << it->alias() << " gets new PRIORITY " << it->priority() << endl;
2385 oldRepo->setPriority( it->priority() );
2386 oldRepoModified = true;
2387 }
2388
2389 // changed url?
2390 {
2391 RepoInfo::url_set newUrls( it->rawBaseUrls() );
2392 urlCredentialExtractor.extract( newUrls ); // Extract! to prevent passwds from disturbing the comparison below
2393 if ( oldRepo->rawBaseUrls() != newUrls )
2394 {
2395 DBG << "Service repo " << it->alias() << " gets new URLs " << newUrls << endl;
2396 oldRepo->setBaseUrls( std::move(newUrls) );
2397 oldRepoModified = true;
2398 }
2399 }
2400
2401 // changed gpg check settings?
2402 // ATM only plugin services can set GPG values.
2403 if ( service.type() == ServiceType::PLUGIN )
2404 {
2405 TriBool ogpg[3]; // Gpg RepoGpg PkgGpg
2406 TriBool ngpg[3];
2407 oldRepo->getRawGpgChecks( ogpg[0], ogpg[1], ogpg[2] );
2408 it-> getRawGpgChecks( ngpg[0], ngpg[1], ngpg[2] );
2409#define Z_CHKGPG(I,N) \
2410 if ( ! sameTriboolState( ogpg[I], ngpg[I] ) ) \
2411 { \
2412 DBG << "Service repo " << it->alias() << " gets new "#N"Check " << ngpg[I] << endl; \
2413 oldRepo->set##N##Check( ngpg[I] ); \
2414 oldRepoModified = true; \
2415 }
2416 Z_CHKGPG( 0, Gpg );
2417 Z_CHKGPG( 1, RepoGpg );
2418 Z_CHKGPG( 2, PkgGpg );
2419#undef Z_CHKGPG
2420 }
2421
2422 // save if modified:
2423 if ( oldRepoModified )
2424 {
2425 modifyRepository( oldRepo->alias(), *oldRepo );
2426 }
2427 }
2428 }
2429
2430 // Unlike reposToEnable, reposToDisable is always cleared after refresh.
2431 if ( ! service.reposToDisableEmpty() )
2432 {
2433 service.clearReposToDisable();
2434 serviceModified = true;
2435 }
2436
2437 // Remember original service request for next refresh
2438 if ( service.repoStates() != newRepoStates )
2439 {
2440 service.setRepoStates( std::move(newRepoStates) );
2441 serviceModified = true;
2442 }
2443
2445 // save service if modified: (unless a plugin service)
2446 if ( service.type() != ServiceType::PLUGIN )
2447 {
2448 if ( service.ttl() )
2449 {
2450 service.setLrf( Date::now() ); // remember last refresh
2451 serviceModified = true; // or use a cookie file
2452 }
2453
2454 if ( serviceModified )
2455 {
2456 // write out modified service file.
2457 modifyService( service.alias(), service );
2458 }
2459 }
2460
2461 if ( uglyHack.first )
2462 {
2463 throw( uglyHack.second ); // intentionally not ZYPP_THROW
2464 }
2465 }
2466
2468
2469 void RepoManager::Impl::modifyService( const std::string & oldAlias, const ServiceInfo & newService )
2470 {
2471 MIL << "Going to modify service " << oldAlias << endl;
2472
2473 // we need a writable copy to link it to the file where
2474 // it is saved if we modify it
2475 ServiceInfo service(newService);
2476
2477 if ( service.type() == ServiceType::PLUGIN )
2478 {
2480 }
2481
2482 const ServiceInfo & oldService = getService(oldAlias);
2483
2484 Pathname location = oldService.filepath();
2485 if( location.empty() )
2486 {
2487 ZYPP_THROW(ServiceException( oldService, _("Can't figure out where the service is stored.") ));
2488 }
2489
2490 // remember: there may multiple services being defined in one file:
2491 ServiceSet tmpSet;
2492 parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
2493
2494 filesystem::assert_dir(location.dirname());
2495 std::ofstream file(location.c_str());
2496 for_(it, tmpSet.begin(), tmpSet.end())
2497 {
2498 if( *it != oldAlias )
2499 it->dumpAsIniOn(file);
2500 }
2501 service.dumpAsIniOn(file);
2502 file.close();
2503 service.setFilepath(location);
2504
2505 _services.erase(oldAlias);
2506 _services.insert(service);
2507 // check for credentials in Urls
2508 UrlCredentialExtractor( _options.rootDir ).collect( service.url() );
2509
2510
2511 // changed properties affecting also repositories
2512 if ( oldAlias != service.alias() // changed alias
2513 || oldService.enabled() != service.enabled() ) // changed enabled status
2514 {
2515 std::vector<RepoInfo> toModify;
2516 getRepositoriesInService(oldAlias, std::back_inserter(toModify));
2517 for_( it, toModify.begin(), toModify.end() )
2518 {
2519 if ( oldService.enabled() != service.enabled() )
2520 {
2521 if ( service.enabled() )
2522 {
2523 // reset to last refreshs state
2524 const auto & last = service.repoStates().find( it->alias() );
2525 if ( last != service.repoStates().end() )
2526 it->setEnabled( last->second.enabled );
2527 }
2528 else
2529 it->setEnabled( false );
2530 }
2531
2532 if ( oldAlias != service.alias() )
2533 it->setService(service.alias());
2534
2535 modifyRepository(it->alias(), *it);
2536 }
2537 }
2538
2540 }
2541
2543
2544 repo::ServiceType RepoManager::Impl::probeService( const Url & url ) const
2545 {
2546 try
2547 {
2548 MediaSetAccess access(url);
2549 if ( access.doesFileExist("/repo/repoindex.xml") )
2551 }
2552 catch ( const media::MediaException &e )
2553 {
2554 ZYPP_CAUGHT(e);
2555 // TranslatorExplanation '%s' is an URL
2556 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
2557 enew.remember(e);
2558 ZYPP_THROW(enew);
2559 }
2560 catch ( const Exception &e )
2561 {
2562 ZYPP_CAUGHT(e);
2563 // TranslatorExplanation '%s' is an URL
2564 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
2565 enew.remember(e);
2566 ZYPP_THROW(enew);
2567 }
2568
2570 }
2571
2572 void RepoManager::Impl::refreshGeoIPData ( const RepoInfo::url_set &urls )
2573 {
2574 try {
2575
2576 if ( !ZConfig::instance().geoipEnabled() ) {
2577 MIL << "GeoIp disabled via ZConfig, not refreshing the GeoIP information." << std::endl;
2578 return;
2579 }
2580
2581 std::vector<std::string> hosts;
2582 for ( const auto &baseUrl : urls ) {
2583 const auto &host = baseUrl.getHost();
2584 if ( zypp::any_of( ZConfig::instance().geoipHostnames(), [&host]( const auto &elem ){ return ( zypp::str::compareCI( host, elem ) == 0 ); } ) ) {
2585 hosts.push_back( host );
2586 break;
2587 }
2588 }
2589
2590 if ( hosts.empty() ) {
2591 MIL << "No configured geoip URL found, not updating geoip data" << std::endl;
2592 return;
2593 }
2594
2595 const auto &geoIPCache = ZConfig::instance().geoipCachePath();
2596
2597 if ( filesystem::assert_dir( geoIPCache ) != 0 ) {
2598 MIL << "Unable to create cache directory for GeoIP." << std::endl;
2599 return;
2600 }
2601
2602 if ( !PathInfo(geoIPCache).userMayRWX() ) {
2603 MIL << "No access rights for the GeoIP cache directory." << std::endl;
2604 return;
2605 }
2606
2607 // remove all older cache entries
2608 filesystem::dirForEachExt( geoIPCache, []( const Pathname &dir, const filesystem::DirEntry &entry ){
2609 if ( entry.type != filesystem::FT_FILE )
2610 return true;
2611
2612 PathInfo pi( dir/entry.name );
2613 auto age = std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t( pi.mtime() );
2614 if ( age < std::chrono::hours(24) )
2615 return true;
2616
2617 MIL << "Removing GeoIP file for " << entry.name << " since it's older than 24hrs." << std::endl;
2618 filesystem::unlink( dir/entry.name );
2619 return true;
2620 });
2621
2622 // go over all found hostnames
2623 std::for_each( hosts.begin(), hosts.end(), [ & ]( const std::string &hostname ) {
2624
2625 // do not query files that are still there
2626 if ( zypp::PathInfo( geoIPCache / hostname ).isExist() ) {
2627 MIL << "Skipping GeoIP request for " << hostname << " since a valid cache entry exists." << std::endl;
2628 return;
2629 }
2630
2631 MIL << "Query GeoIP for " << hostname << std::endl;
2632
2633 zypp::Url url;
2634 try
2635 {
2636 url.setHost(hostname);
2637 url.setScheme("https");
2638 }
2639 catch(const zypp::Exception &e )
2640 {
2641 ZYPP_CAUGHT(e);
2642 MIL << "Ignoring invalid GeoIP hostname: " << hostname << std::endl;
2643 return;
2644 }
2645
2646 MediaSetAccess acc( url );
2647 zypp::ManagedFile file;
2648 try {
2649 // query the file from the server
2650 file = zypp::ManagedFile (acc.provideOptionalFile("/geoip"), filesystem::unlink );
2651
2652 } catch ( const zypp::Exception &e ) {
2653 ZYPP_CAUGHT(e);
2654 MIL << "Failed to query GeoIP from hostname: " << hostname << std::endl;
2655 return;
2656 }
2657 if ( !file->empty() ) {
2658
2659 constexpr auto writeHostToFile = []( const Pathname &fName, const std::string &host ){
2660 std::ofstream out;
2661 out.open( fName.asString(), std::ios_base::trunc );
2662 if ( out.is_open() ) {
2663 out << host << std::endl;
2664 } else {
2665 MIL << "Failed to create/open GeoIP cache file " << fName << std::endl;
2666 }
2667 };
2668
2669 std::string geoipMirror;
2670 try {
2671 xml::Reader reader( *file );
2672 if ( reader.seekToNode( 1, "host" ) ) {
2673 const auto &str = reader.nodeText().asString();
2674
2675 // make a dummy URL to ensure the hostname is valid
2676 zypp::Url testUrl;
2677 testUrl.setHost(str);
2678 testUrl.setScheme("https");
2679
2680 if ( testUrl.isValid() ) {
2681 MIL << "Storing geoIP redirection: " << hostname << " -> " << str << std::endl;
2682 geoipMirror = str;
2683 }
2684
2685 } else {
2686 MIL << "No host entry or empty file returned for GeoIP, remembering for 24hrs" << std::endl;
2687 }
2688 } catch ( const zypp::Exception &e ) {
2689 ZYPP_CAUGHT(e);
2690 MIL << "Empty or invalid GeoIP file, not requesting again for 24hrs" << std::endl;
2691 }
2692
2693 writeHostToFile( geoIPCache / hostname, geoipMirror );
2694 }
2695 });
2696
2697 } catch ( const zypp::Exception &e ) {
2698 ZYPP_CAUGHT(e);
2699 MIL << "Failed to query GeoIP data." << std::endl;
2700 }
2701 }
2702
2704 //
2705 // CLASS NAME : RepoManager
2706 //
2708
2710 : _pimpl( new Impl(opt) )
2711 {}
2712
2714 {}
2715
2716 bool RepoManager::repoEmpty() const
2717 { return _pimpl->repoEmpty(); }
2718
2720 { return _pimpl->repoSize(); }
2721
2723 { return _pimpl->repoBegin(); }
2724
2726 { return _pimpl->repoEnd(); }
2727
2728 RepoInfo RepoManager::getRepo( const std::string & alias ) const
2729 { return _pimpl->getRepo( alias ); }
2730
2731 bool RepoManager::hasRepo( const std::string & alias ) const
2732 { return _pimpl->hasRepo( alias ); }
2733
2734 std::string RepoManager::makeStupidAlias( const Url & url_r )
2735 {
2736 std::string ret( url_r.getScheme() );
2737 if ( ret.empty() )
2738 ret = "repo-";
2739 else
2740 ret += "-";
2741
2742 std::string host( url_r.getHost() );
2743 if ( ! host.empty() )
2744 {
2745 ret += host;
2746 ret += "-";
2747 }
2748
2749 static Date::ValueType serial = Date::now();
2750 ret += Digest::digest( Digest::sha1(), str::hexstring( ++serial ) +url_r.asCompleteString() ).substr(0,8);
2751 return ret;
2752 }
2753
2755 { return _pimpl->metadataStatus( info ); }
2756
2758 { return _pimpl->checkIfToRefreshMetadata( info, url, policy ); }
2759
2760 Pathname RepoManager::metadataPath( const RepoInfo &info ) const
2761 { return _pimpl->metadataPath( info ); }
2762
2763 Pathname RepoManager::packagesPath( const RepoInfo &info ) const
2764 { return _pimpl->packagesPath( info ); }
2765
2767 { return _pimpl->refreshMetadata( info, policy, progressrcv ); }
2768
2769 void RepoManager::cleanMetadata( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2770 { return _pimpl->cleanMetadata( info, progressrcv ); }
2771
2772 void RepoManager::cleanPackages( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2773 { return _pimpl->cleanPackages( info, progressrcv ); }
2774
2775 RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
2776 { return _pimpl->cacheStatus( info ); }
2777
2778 void RepoManager::buildCache( const RepoInfo &info, CacheBuildPolicy policy, const ProgressData::ReceiverFnc & progressrcv )
2779 { return _pimpl->buildCache( info, policy, progressrcv ); }
2780
2781 void RepoManager::cleanCache( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2782 { return _pimpl->cleanCache( info, progressrcv ); }
2783
2784 bool RepoManager::isCached( const RepoInfo &info ) const
2785 { return _pimpl->isCached( info ); }
2786
2787 void RepoManager::loadFromCache( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2788 { return _pimpl->loadFromCache( info, progressrcv ); }
2789
2791 { return _pimpl->cleanCacheDirGarbage( progressrcv ); }
2792
2793 repo::RepoType RepoManager::probe( const Url & url, const Pathname & path ) const
2794 { return _pimpl->probe( url, path ); }
2795
2797 { return _pimpl->probe( url ); }
2798
2799 void RepoManager::addRepository( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2800 { return _pimpl->addRepository( info, progressrcv ); }
2801
2802 void RepoManager::addRepositories( const Url &url, const ProgressData::ReceiverFnc & progressrcv )
2803 { return _pimpl->addRepositories( url, progressrcv ); }
2804
2805 void RepoManager::removeRepository( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
2806 { return _pimpl->removeRepository( info, progressrcv ); }
2807
2808 void RepoManager::modifyRepository( const std::string &alias, const RepoInfo & newinfo, const ProgressData::ReceiverFnc & progressrcv )
2809 { return _pimpl->modifyRepository( alias, newinfo, progressrcv ); }
2810
2811 RepoInfo RepoManager::getRepositoryInfo( const std::string &alias, const ProgressData::ReceiverFnc & progressrcv )
2812 { return _pimpl->getRepositoryInfo( alias, progressrcv ); }
2813
2814 RepoInfo RepoManager::getRepositoryInfo( const Url & url, const url::ViewOption & urlview, const ProgressData::ReceiverFnc & progressrcv )
2815 { return _pimpl->getRepositoryInfo( url, urlview, progressrcv ); }
2816
2817 bool RepoManager::serviceEmpty() const
2818 { return _pimpl->serviceEmpty(); }
2819
2821 { return _pimpl->serviceSize(); }
2822
2824 { return _pimpl->serviceBegin(); }
2825
2827 { return _pimpl->serviceEnd(); }
2828
2829 ServiceInfo RepoManager::getService( const std::string & alias ) const
2830 { return _pimpl->getService( alias ); }
2831
2832 bool RepoManager::hasService( const std::string & alias ) const
2833 { return _pimpl->hasService( alias ); }
2834
2836 { return _pimpl->probeService( url ); }
2837
2838 void RepoManager::addService( const std::string & alias, const Url& url )
2839 { return _pimpl->addService( alias, url ); }
2840
2841 void RepoManager::addService( const ServiceInfo & service )
2842 { return _pimpl->addService( service ); }
2843
2844 void RepoManager::removeService( const std::string & alias )
2845 { return _pimpl->removeService( alias ); }
2846
2847 void RepoManager::removeService( const ServiceInfo & service )
2848 { return _pimpl->removeService( service ); }
2849
2851 { return _pimpl->refreshServices( options_r ); }
2852
2853 void RepoManager::refreshService( const std::string & alias, const RefreshServiceOptions & options_r )
2854 { return _pimpl->refreshService( alias, options_r ); }
2855
2856 void RepoManager::refreshService( const ServiceInfo & service, const RefreshServiceOptions & options_r )
2857 { return _pimpl->refreshService( service, options_r ); }
2858
2859 void RepoManager::modifyService( const std::string & oldAlias, const ServiceInfo & service )
2860 { return _pimpl->modifyService( oldAlias, service ); }
2861
2863 { return _pimpl->refreshGeoIPData( urls ); }
2864
2866
2867 std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
2868 { return str << *obj._pimpl; }
2869
2871} // namespace zypp
#define Z_CHKGPG(I, N)
const Pathname & _root
Definition: RepoManager.cc:151
ServiceSet & _services
Definition: RepoManager.cc:458
std::string targetDistro
Definition: RepoManager.cc:286
#define OPT_PROGRESS
Definition: RepoManager.cc:66
media::MediaAccessId _mid
Definition: RepoManager.cc:193
RepoInfoList repos
Definition: RepoManager.cc:285
scoped_ptr< media::CredentialManager > _cmPtr
Definition: RepoManager.cc:152
#define OUTS(V)
RepoManager implementation.
std::ostream & operator<<(std::ostream &str, const RepoManager::Impl &obj)
Stream output.
Definition: RepoManager.cc:737
Progress callback from another progress.
Definition: progressdata.h:394
Store and operate on date (time_t).
Definition: Date.h:33
time_t Duration
Definition: Date.h:39
static const ValueType day
Definition: Date.h:44
time_t ValueType
Definition: Date.h:38
static Date now()
Return the current time.
Definition: Date.h:78
Integral type with defined initial value when default constructed.
std::string digest()
get hex string representation of the digest
Definition: Digest.cc:206
static const std::string & sha1()
sha1
Definition: Digest.cc:40
Base class for Exception.
Definition: Exception.h:146
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition: Exception.cc:91
std::string asUserString() const
Translated error message as string suitable for the user.
Definition: Exception.cc:82
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
std::vector< std::string > Arguments
Writing the zypp history file.
Definition: HistoryLog.h:57
void modifyRepository(const RepoInfo &oldrepo, const RepoInfo &newrepo)
Log certain modifications to a repository.
Definition: HistoryLog.cc:312
void addRepository(const RepoInfo &repo)
Log a newly added repository.
Definition: HistoryLog.cc:289
void removeRepository(const RepoInfo &repo)
Log recently removed repository.
Definition: HistoryLog.cc:301
Media access layer responsible for handling files distributed on a set of media with media change and...
static ManagedFile provideFileFromUrl(const Url &file_url, ProvideFileOptions options=PROVIDE_DEFAULT)
Provides file from url.
Maintain [min,max] and counter (value) for progress counting.
Definition: progressdata.h:132
void sendTo(const ReceiverFnc &fnc_r)
Set ReceiverFnc.
Definition: progressdata.h:229
void name(const std::string &name_r)
Set counter name.
Definition: progressdata.h:225
function< bool(const ProgressData &)> ReceiverFnc
Most simple version of progress reporting The percentage in most cases.
Definition: progressdata.h:140
What is known about a repository.
Definition: RepoInfo.h:72
std::list< Url > url_set
Definition: RepoInfo.h:103
Pathname metadataPath() const
Path where this repo metadata was read from.
Definition: RepoInfo.cc:680
bool baseUrlsEmpty() const
whether repository urls are available
Definition: RepoInfo.cc:743
void setBaseUrl(const Url &url)
Clears current base URL list and adds url.
Definition: RepoInfo.cc:643
repo::RepoType type() const
Type of repository,.
Definition: RepoInfo.cc:689
urls_size_type baseUrlsSize() const
number of repository urls
Definition: RepoInfo.cc:740
Url url() const
Pars pro toto: The first repository url.
Definition: RepoInfo.h:131
urls_const_iterator baseUrlsEnd() const
iterator that points at end of repository urls
Definition: RepoInfo.cc:737
void setPackagesPath(const Pathname &path)
set the path where the local packages are stored
Definition: RepoInfo.cc:665
virtual std::ostream & dumpAsIniOn(std::ostream &str) const
Write this RepoInfo object into str in a .repo file format.
Definition: RepoInfo.cc:933
Pathname path() const
Repository path.
Definition: RepoInfo.cc:722
url_set baseUrls() const
The complete set of repository urls.
Definition: RepoInfo.cc:716
bool requireStatusWithMediaFile() const
Returns true if this repository requires the media.1/media file to be included in the metadata status...
Definition: RepoInfo.cc:1051
bool usesAutoMethadataPaths() const
Whether metadataPath uses AUTO% setup.
Definition: RepoInfo.cc:686
void setProbedType(const repo::RepoType &t) const
This allows to adjust the RepoType lazy, from NONE to some probed value, even for const objects.
Definition: RepoInfo.cc:658
urls_const_iterator baseUrlsBegin() const
iterator that points at begin of repository urls
Definition: RepoInfo.cc:734
void setMetadataPath(const Pathname &path)
Set the path where the local metadata is stored.
Definition: RepoInfo.cc:662
Pathname packagesPath() const
Path where this repo packages are cached.
Definition: RepoInfo.cc:683
transform_iterator< repo::RepoVariablesUrlReplacer, url_set::const_iterator > urls_const_iterator
Definition: RepoInfo.h:105
std::string targetDistribution() const
Distribution for which is this repository meant.
Definition: RepoInfo.cc:728
void setType(const repo::RepoType &t)
set the repository type
Definition: RepoInfo.cc:655
Track changing files or directories.
Definition: RepoStatus.h:41
static RepoStatus fromCookieFile(const Pathname &path)
Reads the status from a cookie file.
Definition: RepoStatus.cc:194
Date timestamp() const
The time the data were changed the last time.
Definition: RepoStatus.cc:225
bool empty() const
Whether the status is empty (empty checksum)
Definition: RepoStatus.cc:222
void saveToCookieFile(const Pathname &path_r) const
Save the status information to a cookie file.
Definition: RepoStatus.cc:212
static const std::string & systemRepoAlias()
Reserved system repository alias @System .
Definition: Repository.cc:37
void eraseFromPool()
Remove this Repository from its Pool.
Definition: Repository.cc:297
Service data.
Definition: ServiceInfo.h:37
repo::ServiceType type() const
Service type.
Definition: ServiceInfo.cc:108
Date::Duration ttl() const
Sugested TTL between two metadata auto-refreshs.
Definition: ServiceInfo.cc:112
void setLrf(Date lrf_r)
Set date of last refresh.
Definition: ServiceInfo.cc:117
Date lrf() const
Date of last refresh (if known).
Definition: ServiceInfo.cc:116
bool repoToDisableFind(const std::string &alias_r) const
Whether alias_r is mentioned in ReposToDisable.
Definition: ServiceInfo.cc:145
bool repoToEnableFind(const std::string &alias_r) const
Whether alias_r is mentioned in ReposToEnable.
Definition: ServiceInfo.cc:124
const RepoStates & repoStates() const
Access the remembered repository states.
Definition: ServiceInfo.cc:161
Url url() const
The service url.
Definition: ServiceInfo.cc:99
void setProbedType(const repo::ServiceType &t) const
Lazy init service type.
Definition: ServiceInfo.cc:110
std::map< std::string, RepoState > RepoStates
Definition: ServiceInfo.h:185
Url rawUrl() const
The service raw url (no variables replaced)
Definition: ServiceInfo.cc:102
void addRepoToEnable(const std::string &alias_r)
Add alias_r to the set of ReposToEnable.
Definition: ServiceInfo.cc:127
void clearReposToDisable()
Clear the set of ReposToDisable.
Definition: ServiceInfo.cc:157
void delRepoToEnable(const std::string &alias_r)
Remove alias_r from the set of ReposToEnable.
Definition: ServiceInfo.cc:133
virtual std::ostream & dumpAsIniOn(std::ostream &str) const
Writes ServiceInfo to stream in ".service" format.
Definition: ServiceInfo.cc:173
void setRepoStates(RepoStates newStates_r)
Remember a new set of repository states.
Definition: ServiceInfo.cc:162
bool reposToDisableEmpty() const
Definition: ServiceInfo.cc:140
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition: Target.cc:102
Url manipulation class.
Definition: Url.h:92
std::string getScheme() const
Returns the scheme name of the URL.
Definition: Url.cc:533
std::string asCompleteString() const
Returns a complete string representation of the Url object.
Definition: Url.cc:505
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:497
std::string getPathName(EEncoding eflag=zypp::url::E_DECODED) const
Returns the path name from the URL.
Definition: Url.cc:604
void setPathName(const std::string &path, EEncoding eflag=zypp::url::E_DECODED)
Set the path name.
Definition: Url.cc:764
std::string getHost(EEncoding eflag=zypp::url::E_DECODED) const
Returns the hostname or IP from the URL authority.
Definition: Url.cc:588
void setHost(const std::string &host)
Set the hostname or IP in the URL authority.
Definition: Url.cc:748
static bool schemeIsLocal(const std::string &scheme_r)
hd cd dvd dir file iso
Definition: Url.cc:457
bool isValid() const
Verifies the Url.
Definition: Url.cc:489
void setPassword(const std::string &pass, EEncoding eflag=zypp::url::E_DECODED)
Set the password in the URL authority.
Definition: Url.cc:739
static bool schemeIsPlugin(const std::string &scheme_r)
plugin
Definition: Url.cc:481
bool hasCredentialsInAuthority() const
Returns true if username and password are encoded in the authority component.
Definition: Url.h:380
void setScheme(const std::string &scheme)
Set the scheme name in the URL.
Definition: Url.cc:668
static bool schemeIsDownloading(const std::string &scheme_r)
http https ftp sftp tftp
Definition: Url.cc:475
static bool schemeIsVolatile(const std::string &scheme_r)
cd dvd
Definition: Url.cc:469
unsigned repo_refresh_delay() const
Amount of time in minutes that must pass before another refresh.
Definition: ZConfig.cc:1161
Pathname builtinRepoSolvfilesPath() const
The builtin config file value.
Definition: ZConfig.cc:1094
bool repo_add_probe() const
Whether repository urls should be probed.
Definition: ZConfig.cc:1158
Pathname geoipCachePath() const
Path where the geoip caches are kept (/var/cache/zypp/geoip)
Definition: ZConfig.cc:1132
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:922
Pathname builtinRepoPackagesPath() const
The builtin config file value.
Definition: ZConfig.cc:1097
Pathname builtinRepoMetadataPath() const
The builtin config file value.
Definition: ZConfig.cc:1091
Wrapper class for stat/lstat.
Definition: PathInfo.h:221
time_t mtime() const
Definition: PathInfo.h:376
bool userMayRX() const
Definition: PathInfo.h:350
const Pathname & path() const
Return current Pathname.
Definition: PathInfo.h:246
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:281
const std::string & asString() const
Return current Pathname as String.
Definition: PathInfo.h:248
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition: Pathname.h:173
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:124
const char * c_str() const
String representation.
Definition: Pathname.h:110
const std::string & asString() const
String representation.
Definition: Pathname.h:91
std::string basename() const
Return the last component of this path.
Definition: Pathname.h:128
bool empty() const
Test for an empty path.
Definition: Pathname.h:114
static Pathname assertprefix(const Pathname &root_r, const Pathname &path_r)
Return path_r prefixed with root_r, unless it is already prefixed.
Definition: Pathname.cc:272
Provide a new empty temporary directory and recursively delete it when no longer needed.
Definition: TmpPath.h:178
static TmpDir makeSibling(const Pathname &sibling_r)
Provide a new empty temporary directory as sibling.
Definition: TmpPath.cc:295
Just inherits Exception to separate media exceptions.
Manages access to the 'physical' media, e.g CDROM drives, Disk volumes, directory trees,...
Definition: MediaManager.h:454
MediaAccessId open(const Url &url, const Pathname &preferred_attach_point="")
Opens the media access for specified with the url.
void attach(MediaAccessId accessId)
Attach the media using the concrete handler (checks all devices).
void close(MediaAccessId accessId)
Close the media access with specified id.
void release(MediaAccessId accessId, const std::string &ejectDev="")
Release the attached media and optionally eject.
Pathname localPath(MediaAccessId accessId, const Pathname &pathname) const
Shortcut for 'localRoot() + pathname', but returns an empty pathname if media is not attached.
Read repository data from a .repo file.
Read service data from a .service file.
Repository already exists and some unique attribute can't be duplicated.
Exception for repository handling.
Definition: RepoException.h:38
std::string label() const
Label for use in messages for the user interface.
std::string escaped_alias() const
Same as alias(), just escaped in a way to be a valid file name.
void setFilepath(const Pathname &filename)
set the path to the .repo file
void setAlias(const std::string &alias)
set the repository alias
Definition: RepoInfoBase.cc:94
Pathname filepath() const
File where this repo was read from.
bool enabled() const
If enabled is false, then this repository must be ignored as if does not exists, except when checking...
std::string alias() const
unique identifier for this source.
Thrown when the repo alias is found to be invalid.
thrown when it was impossible to determine an alias for this repo.
Definition: RepoException.h:92
thrown when it was impossible to determine one url for this repo.
Definition: RepoException.h:79
The repository cache is not built yet so you can't create the repostories from the cache.
Definition: RepoException.h:66
thrown when it was impossible to match a repository
thrown when it was impossible to determine this repo type.
Service already exists and some unique attribute can't be duplicated.
Base Exception for service handling.
Thrown when the repo alias is found to be invalid.
Service without alias was used in an operation.
Service has no or invalid url defined.
Service plugin has trouble providing the metadata but this should not be treated as error.
Retrieval of repository list for a service.
Definition: ServiceRepos.h:26
Downloader for SUSETags (YaST2) repositories Encapsulates all the knowledge of which files have to be...
Definition: Downloader.h:35
RepoStatus status(MediaSetAccess &media) override
Status of the remote repository.
Definition: Downloader.cc:35
Downloader for YUM (rpm-nmd) repositories Encapsulates all the knowledge of which files have to be do...
Definition: Downloader.h:41
RepoStatus status(MediaSetAccess &media_r) override
Status of the remote repository.
Definition: Downloader.cc:205
Lightweight repository attribute value lookup.
Definition: LookupAttr.h:258
void reposErase(const std::string &alias_r)
Remove a Repository named alias_r.
Definition: Pool.h:112
Repository addRepoSolv(const Pathname &file_r, const std::string &name_r)
Load Solvables from a solv-file into a Repository named name_r.
Definition: Pool.cc:185
static Pool instance()
Singleton ctor.
Definition: Pool.h:55
static const SolvAttr repositoryToolVersion
Definition: SolvAttr.h:181
Regular expression.
Definition: Regex.h:95
xmlTextReader based interface to iterate xml streams.
Definition: Reader.h:96
Repository metadata verification beyond GPG.
String related utilities and Regular expression matching.
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
Definition: NonCopyable.h:26
std::string asString(TInt val, char zero='0', char one='1')
For printing bits.
Definition: Bit.h:57
bool ZYPP_PLUGIN_APPDATA_FORCE_COLLECT()
To trigger appdata refresh unconditionally.
Definition: RepoManager.cc:79
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition: PathInfo.cc:605
int unlink(const Pathname &path)
Like 'unlink'.
Definition: PathInfo.cc:700
int recursive_rmdir(const Pathname &path)
Like 'rm -r DIR'.
Definition: PathInfo.cc:412
int assert_dir(const Pathname &path, unsigned mode)
Like 'mkdir -p'.
Definition: PathInfo.cc:319
int touch(const Pathname &path)
Change file's modification and access times.
Definition: PathInfo.cc:1234
int exchange(const Pathname &lpath, const Pathname &rpath)
Exchanges two files or directories.
Definition: PathInfo.cc:756
int dirForEachExt(const Pathname &dir_r, const function< bool(const Pathname &, const DirEntry &)> &fnc_r)
Simiar to.
Definition: PathInfo.cc:593
unsigned int MediaAccessId
Media manager access Id type.
Definition: MediaSource.h:29
void updateSolvFileIndex(const Pathname &solvfile_r)
Create solv file content digest for zypper bash completion.
Definition: Pool.cc:286
std::string & replaceAll(std::string &str_r, const std::string &from_r, const std::string &to_r)
Replace all occurrences of from_r with to_r in str_r (inplace).
Definition: String.cc:330
std::string numstring(char n, int w=0)
Definition: String.h:289
bool regex_match(const std::string &s, smatch &matches, const regex &regex)
\relates regex \ingroup ZYPP_STR_REGEX \relates regex \ingroup ZYPP_STR_REGEX
Definition: Regex.h:70
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition: String.h:429
std::string hexstring(char n, int w=4)
Definition: String.h:324
int compareCI(const C_Str &lhs, const C_Str &rhs)
Definition: String.h:984
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2
std::list< RepoInfo > readRepoFile(const Url &repo_file)
Parses repo_file and returns a list of RepoInfo objects corresponding to repositories found within th...
Definition: RepoManager.cc:469
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition: ManagedFile.h:27
bool any_of(const Container &c, Fnc &&cb)
Definition: Algorithm.h:76
std::ostream & operator<<(std::ostream &str, const SerialNumber &obj)
Definition: SerialNumber.cc:52
static bool warning(const std::string &msg_r, const UserData &userData_r=UserData())
send warning text
static bool error(const std::string &msg_r, const UserData &userData_r=UserData())
send error text
Repo manager settings.
Definition: RepoManager.h:54
static RepoManagerOptions makeTestSetup(const Pathname &root_r)
Test setup adjusting all paths to be located below one root_r directory.
Definition: RepoManager.cc:498
Pathname rootDir
remembers root_r value for later use
Definition: RepoManager.h:96
Pathname repoPackagesCachePath
Definition: RepoManager.h:82
RepoManagerOptions(const Pathname &root_r=Pathname())
Default ctor following ZConfig global settings.
Definition: RepoManager.cc:484
Functor thats filter RepoInfo by service which it belongs to.
Definition: RepoManager.h:648
creates and provides information about known sources.
Definition: RepoManager.cc:533
bool hasRepo(const std::string &alias) const
Definition: RepoManager.cc:593
ServiceSet::const_iterator ServiceConstIterator
Definition: RepoManager.h:115
bool serviceEmpty() const
Definition: RepoManager.cc:648
bool hasService(const std::string &alias) const
Definition: RepoManager.cc:653
RefreshCheckStatus
Possibly return state of checkIfRefreshMEtadata function.
Definition: RepoManager.h:196
RefreshCheckStatus checkIfToRefreshMetadata(const RepoInfo &info, const Url &url, RawMetadataRefreshPolicy policy)
Impl * clone() const
clone for RWCOW_pointer
Definition: RepoManager.cc:731
void refreshService(const ServiceInfo &service, const RefreshServiceOptions &options_r)
Definition: RepoManager.cc:674
void addRepository(const RepoInfo &info, OPT_PROGRESS)
RepoSet::const_iterator RepoConstIterator
Definition: RepoManager.h:120
RepoInfo getRepositoryInfo(const Url &url, const url::ViewOption &urlview, OPT_PROGRESS)
void addService(const std::string &alias, const Url &url)
Definition: RepoManager.cc:664
std::string generateFilename(const ServiceInfo &info) const
Definition: RepoManager.cc:691
bool isCached(const RepoInfo &info) const
Definition: RepoManager.cc:628
void cleanPackages(const RepoInfo &info, OPT_PROGRESS, bool isAutoClean=false)
void modifyRepository(const std::string &alias, const RepoInfo &newinfo_r, OPT_PROGRESS)
void loadFromCache(const RepoInfo &info, OPT_PROGRESS)
void refreshMetadata(const RepoInfo &info, RawMetadataRefreshPolicy policy, OPT_PROGRESS)
void removeService(const std::string &alias)
void refreshService(const std::string &alias, const RefreshServiceOptions &options_r)
void buildCache(const RepoInfo &info, CacheBuildPolicy policy, OPT_PROGRESS)
RepoManager(const RepoManagerOptions &options=RepoManagerOptions())
RepoInfo getRepo(const std::string &alias) const
Definition: RepoManager.cc:596
repo::ServiceType probeService(const Url &url) const
RWCOW_pointer< Impl > _pimpl
Pointer to implementation.
Definition: RepoManager.h:704
RepoInfo getRepositoryInfo(const std::string &alias, OPT_PROGRESS)
void cleanCacheDirGarbage(OPT_PROGRESS)
RepoSet & reposManip()
Definition: RepoManager.cc:717
void addService(const ServiceInfo &service)
bool repoEmpty() const
Definition: RepoManager.cc:588
void init_knownRepositories()
Pathname metadataPath(const RepoInfo &info) const
Definition: RepoManager.cc:603
ServiceSet::size_type ServiceSizeType
Definition: RepoManager.h:116
std::set< RepoInfo > RepoSet
RepoInfo typedefs.
Definition: RepoManager.h:119
Pathname packagesPath(const RepoInfo &info) const
Definition: RepoManager.cc:606
DefaultIntegral< bool, false > _reposDirty
Definition: RepoManager.cc:724
RepoManagerOptions _options
Definition: RepoManager.cc:720
RepoStatus cacheStatus(const RepoInfo &info) const
Definition: RepoManager.cc:631
ServiceSet _services
Definition: RepoManager.cc:722
void touchIndexFile(const RepoInfo &info)
repo::RepoType probeCache(const Pathname &path_r) const
void refreshGeoIp(const RepoInfo::url_set &urls)
void modifyService(const std::string &oldAlias, const ServiceInfo &newService)
ServiceConstIterator serviceEnd() const
Definition: RepoManager.cc:651
ServiceConstIterator serviceBegin() const
Definition: RepoManager.cc:650
void cleanCache(const RepoInfo &info, OPT_PROGRESS)
repo::RepoType probe(const Url &url, const Pathname &path=Pathname()) const
void removeRepository(const RepoInfo &info, OPT_PROGRESS)
RepoSizeType repoSize() const
Definition: RepoManager.cc:589
void removeService(const ServiceInfo &service)
Definition: RepoManager.cc:668
ServiceSizeType serviceSize() const
Definition: RepoManager.cc:649
RepoSet::size_type RepoSizeType
Definition: RepoManager.h:121
void saveService(ServiceInfo &service) const
void addRepositories(const Url &url, OPT_PROGRESS)
void cleanMetadata(const RepoInfo &info, OPT_PROGRESS)
ServiceInfo getService(const std::string &alias) const
Definition: RepoManager.cc:656
Pathname generateNonExistingName(const Pathname &dir, const std::string &basefilename) const
void refreshServices(const RefreshServiceOptions &options_r)
RepoConstIterator repoBegin() const
Definition: RepoManager.cc:590
const RepoSet & repos() const
Iterate the known repositories.
Definition: RepoManager.cc:716
void init_knownServices()
Impl(const RepoManagerOptions &opt)
Definition: RepoManager.cc:535
RefreshServiceFlags RefreshServiceOptions
Options tuning RefreshService.
Definition: RepoManager.h:150
void getRepositoriesInService(const std::string &alias, OutputIterator out) const
Definition: RepoManager.cc:704
std::set< ServiceInfo > ServiceSet
ServiceInfo typedefs.
Definition: RepoManager.h:114
RepoConstIterator repoEnd() const
Definition: RepoManager.cc:591
void setCacheStatus(const RepoInfo &info, const RepoStatus &status)
Definition: RepoManager.cc:694
RepoStatus metadataStatus(const RepoInfo &info) const
static std::string makeStupidAlias(const Url &url_r=Url())
Some stupid string but suitable as alias for your url if nothing better is available.
std::string generateFilename(const RepoInfo &info) const
Definition: RepoManager.cc:688
PluginRepoverification _pluginRepoverification
Definition: RepoManager.cc:726
void refreshGeoIPData(const RepoInfo::url_set &urls)
Listentry returned by readdir.
Definition: PathInfo.h:501
Temporarily disable MediaChangeReport Sometimes helpful to suppress interactive messages connected to...
Repository type enumeration.
Definition: RepoType.h:28
static const RepoType YAST2
Definition: RepoType.h:30
Type toEnum() const
Definition: RepoType.h:48
static const RepoType RPMMD
Definition: RepoType.h:29
static const RepoType NONE
Definition: RepoType.h:32
static const RepoType RPMPLAINDIR
Definition: RepoType.h:31
Service type enumeration.
Definition: ServiceType.h:27
static const ServiceType NONE
No service set.
Definition: ServiceType.h:34
static const ServiceType RIS
Repository Index Service (RIS) (formerly known as 'Novell Update' (NU) service)
Definition: ServiceType.h:32
static const ServiceType PLUGIN
Plugin services are scripts installed on your system that provide the package manager with repositori...
Definition: ServiceType.h:43
Convenient building of std::string with boost::format.
Definition: String.h:253
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition: String.h:212
Url::asString() view options.
Definition: UrlBase.h:40
#define ZYPP_LOCAL
Definition: Globals.h:59
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:440
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:436
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:428
#define PL_(MSG1, MSG2, N)
Definition: Gettext.h:40
#define _(MSG)
Definition: Gettext.h:37
#define DBG
Definition: Logger.h:95
#define MIL
Definition: Logger.h:96
#define ERR
Definition: Logger.h:98
#define WAR
Definition: Logger.h:97