libzypp 17.31.28
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 Url repoFileUrl { replaceVars(repo_file) };
473
474 ManagedFile local = MediaSetAccess::provideFileFromUrl( repoFileUrl );
475 DBG << "reading repo file " << repoFileUrl << ", local path: " << local << endl;
476
477 return repositories_in_file(local);
478 }
479
481 //
482 // class RepoManagerOptions
483 //
485
487 {
489 repoRawCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoMetadataPath() );
490 repoSolvCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoSolvfilesPath() );
491 repoPackagesCachePath = Pathname::assertprefix( root_r, ZConfig::instance().repoPackagesPath() );
496
497 rootDir = root_r;
498 }
499
501 {
503 ret.repoCachePath = root_r;
504 ret.repoRawCachePath = root_r/"raw";
505 ret.repoSolvCachePath = root_r/"solv";
506 ret.repoPackagesCachePath = root_r/"packages";
507 ret.knownReposPath = root_r/"repos.d";
508 ret.knownServicesPath = root_r/"services.d";
509 ret.pluginsPath = root_r/"plugins";
510 ret.rootDir = root_r;
511 return ret;
512 }
513
514 std:: ostream & operator<<( std::ostream & str, const RepoManagerOptions & obj )
515 {
516#define OUTS(X) str << " " #X "\t" << obj.X << endl
517 str << "RepoManagerOptions (" << obj.rootDir << ") {" << endl;
518 OUTS( repoRawCachePath );
519 OUTS( repoSolvCachePath );
520 OUTS( repoPackagesCachePath );
521 OUTS( knownReposPath );
522 OUTS( knownServicesPath );
523 OUTS( pluginsPath );
524 str << "}" << endl;
525#undef OUTS
526 return str;
527 }
528
535 {
536 public:
538 : _options(opt)
539 , _pluginRepoverification( _options.pluginsPath/"repoverification", _options.rootDir )
540 {
541 init_knownServices();
542 init_knownRepositories();
543 }
544
546 {
547 // trigger appdata refresh if some repos change
548 if ( ( _reposDirty || env::ZYPP_PLUGIN_APPDATA_FORCE_COLLECT() )
549 && geteuid() == 0 && ( _options.rootDir.empty() || _options.rootDir == "/" ) )
550 {
551 try {
552 std::list<Pathname> entries;
553 filesystem::readdir( entries, _options.pluginsPath/"appdata", false );
554 if ( ! entries.empty() )
555 {
557 cmd.push_back( "<" ); // discard stdin
558 cmd.push_back( ">" ); // discard stdout
559 cmd.push_back( "PROGRAM" ); // [2] - fix index below if changing!
560 for ( const auto & rinfo : repos() )
561 {
562 if ( ! rinfo.enabled() )
563 continue;
564 cmd.push_back( "-R" );
565 cmd.push_back( rinfo.alias() );
566 cmd.push_back( "-t" );
567 cmd.push_back( rinfo.type().asString() );
568 cmd.push_back( "-p" );
569 cmd.push_back( (rinfo.metadataPath()/rinfo.path()).asString() ); // bsc#1197684: path to the repodata/ directory inside the cache
570 }
571
572 for_( it, entries.begin(), entries.end() )
573 {
574 PathInfo pi( *it );
575 //DBG << "/tmp/xx ->" << pi << endl;
576 if ( pi.isFile() && pi.userMayRX() )
577 {
578 // trigger plugin
579 cmd[2] = pi.asString(); // [2] - PROGRAM
580 ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
581 }
582 }
583 }
584 }
585 catch (...) {} // no throw in dtor
586 }
587 }
588
589 public:
590 bool repoEmpty() const { return repos().empty(); }
591 RepoSizeType repoSize() const { return repos().size(); }
592 RepoConstIterator repoBegin() const { return repos().begin(); }
593 RepoConstIterator repoEnd() const { return repos().end(); }
594
595 bool hasRepo( const std::string & alias ) const
596 { return foundAliasIn( alias, repos() ); }
597
598 RepoInfo getRepo( const std::string & alias ) const
599 {
600 RepoConstIterator it( findAlias( alias, repos() ) );
601 return it == repos().end() ? RepoInfo::noRepo : *it;
602 }
603
604 public:
605 Pathname metadataPath( const RepoInfo & info ) const
606 { return rawcache_path_for_repoinfo( _options, info ); }
607
608 Pathname packagesPath( const RepoInfo & info ) const
609 { return packagescache_path_for_repoinfo( _options, info ); }
610
611 RepoStatus metadataStatus( const RepoInfo & info ) const;
612
613 RefreshCheckStatus checkIfToRefreshMetadata( const RepoInfo & info, const Url & url, RawMetadataRefreshPolicy policy );
614
616
617 void cleanMetadata( const RepoInfo & info, OPT_PROGRESS );
618
619 void cleanPackages( const RepoInfo & info, OPT_PROGRESS, bool isAutoClean = false );
620
621 void buildCache( const RepoInfo & info, CacheBuildPolicy policy, OPT_PROGRESS );
622
623 repo::RepoType probe( const Url & url, const Pathname & path = Pathname() ) const;
624 repo::RepoType probeCache( const Pathname & path_r ) const;
625
627
628 void cleanCache( const RepoInfo & info, OPT_PROGRESS );
629
630 bool isCached( const RepoInfo & info ) const
631 { return PathInfo(solv_path_for_repoinfo( _options, info ) / "solv").isExist(); }
632
633 RepoStatus cacheStatus( const RepoInfo & info ) const
634 { return RepoStatus::fromCookieFile(solv_path_for_repoinfo(_options, info) / "cookie"); }
635
636 void loadFromCache( const RepoInfo & info, OPT_PROGRESS );
637
638 void addRepository( const RepoInfo & info, OPT_PROGRESS );
639
640 void addRepositories( const Url & url, OPT_PROGRESS );
641
643
644 void modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, OPT_PROGRESS );
645
646 RepoInfo getRepositoryInfo( const std::string & alias, OPT_PROGRESS );
648
649 public:
650 bool serviceEmpty() const { return _services.empty(); }
651 ServiceSizeType serviceSize() const { return _services.size(); }
652 ServiceConstIterator serviceBegin() const { return _services.begin(); }
653 ServiceConstIterator serviceEnd() const { return _services.end(); }
654
655 bool hasService( const std::string & alias ) const
656 { return foundAliasIn( alias, _services ); }
657
658 ServiceInfo getService( const std::string & alias ) const
659 {
660 ServiceConstIterator it( findAlias( alias, _services ) );
661 return it == _services.end() ? ServiceInfo::noService : *it;
662 }
663
664 public:
665 void addService( const ServiceInfo & service );
666 void addService( const std::string & alias, const Url & url )
667 { addService( ServiceInfo( alias, url ) ); }
668
669 void removeService( const std::string & alias );
670 void removeService( const ServiceInfo & service )
671 { removeService( service.alias() ); }
672
673 void refreshServices( const RefreshServiceOptions & options_r );
674
675 void refreshService( const std::string & alias, const RefreshServiceOptions & options_r );
676 void refreshService( const ServiceInfo & service, const RefreshServiceOptions & options_r )
677 { refreshService( service.alias(), options_r ); }
678
679 void modifyService( const std::string & oldAlias, const ServiceInfo & newService );
680
681 repo::ServiceType probeService( const Url & url ) const;
682
684
685 private:
686 void saveService( ServiceInfo & service ) const;
687
688 Pathname generateNonExistingName( const Pathname & dir, const std::string & basefilename ) const;
689
690 std::string generateFilename( const RepoInfo & info ) const
691 { return filenameFromAlias( info.alias(), "repo" ); }
692
693 std::string generateFilename( const ServiceInfo & info ) const
694 { return filenameFromAlias( info.alias(), "service" ); }
695
696 void setCacheStatus( const RepoInfo & info, const RepoStatus & status )
697 {
698 Pathname base = solv_path_for_repoinfo( _options, info );
699 filesystem::assert_dir(base);
700 status.saveToCookieFile( base / "cookie" );
701 }
702
703 void touchIndexFile( const RepoInfo & info );
704
705 template<typename OutputIterator>
706 void getRepositoriesInService( const std::string & alias, OutputIterator out ) const
707 {
708 MatchServiceAlias filter( alias );
709 std::copy( boost::make_filter_iterator( filter, repos().begin(), repos().end() ),
710 boost::make_filter_iterator( filter, repos().end(), repos().end() ),
711 out);
712 }
713
714 private:
717
718 const RepoSet & repos() const { return _reposX; }
719 RepoSet & reposManip() { if ( ! _reposDirty ) _reposDirty = true; return _reposX; }
720
721 private:
725
727
729
730 private:
731 friend Impl * rwcowClone<Impl>( const Impl * rhs );
733 Impl * clone() const
734 { return new Impl( *this ); }
735 };
737
739 inline std::ostream & operator<<( std::ostream & str, const RepoManager::Impl & obj )
740 { return str << "RepoManager::Impl"; }
741
743
744 void RepoManager::Impl::saveService( ServiceInfo & service ) const
745 {
746 filesystem::assert_dir( _options.knownServicesPath );
747 Pathname servfile = generateNonExistingName( _options.knownServicesPath,
748 generateFilename( service ) );
749 service.setFilepath( servfile );
750
751 MIL << "saving service in " << servfile << endl;
752
753 std::ofstream file( servfile.c_str() );
754 if ( !file )
755 {
756 // TranslatorExplanation '%s' is a filename
757 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
758 }
759 service.dumpAsIniOn( file );
760 MIL << "done" << endl;
761 }
762
778 Pathname RepoManager::Impl::generateNonExistingName( const Pathname & dir,
779 const std::string & basefilename ) const
780 {
781 std::string final_filename = basefilename;
782 int counter = 1;
783 while ( PathInfo(dir + final_filename).isExist() )
784 {
785 final_filename = basefilename + "_" + str::numstring(counter);
786 ++counter;
787 }
788 return dir + Pathname(final_filename);
789 }
790
792
793 void RepoManager::Impl::init_knownServices()
794 {
795 Pathname dir = _options.knownServicesPath;
796 std::list<Pathname> entries;
797 if (PathInfo(dir).isExist())
798 {
799 if ( filesystem::readdir( entries, dir, false ) != 0 )
800 {
801 // TranslatorExplanation '%s' is a pathname
802 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir.c_str())));
803 }
804
805 //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
806 for_(it, entries.begin(), entries.end() )
807 {
808 parser::ServiceFileReader(*it, ServiceCollector(_services));
809 }
810 }
811
812 repo::PluginServices(_options.pluginsPath/"services", ServiceCollector(_services));
813 }
814
816 namespace {
823 inline void cleanupNonRepoMetadtaFolders( const Pathname & cachePath_r,
824 const Pathname & defaultCachePath_r,
825 const std::list<std::string> & repoEscAliases_r )
826 {
828 return;
829
830 if ( cachePath_r != defaultCachePath_r )
831 return;
832
833 std::list<std::string> entries;
834 if ( filesystem::readdir( entries, cachePath_r, false ) == 0 )
835 {
836 entries.sort();
837 std::set<std::string> oldfiles;
838 set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
839 std::inserter( oldfiles, oldfiles.end() ) );
840
841 // bsc#1178966: Files or symlinks here have been created by the user
842 // for whatever purpose. It's our cache, so we purge them now before
843 // they may later conflict with directories we need.
844 PathInfo pi;
845 for ( const std::string & old : oldfiles )
846 {
847 if ( old == Repository::systemRepoAlias() ) // don't remove the @System solv file
848 continue;
849 pi( cachePath_r/old );
850 if ( pi.isDir() )
852 else
853 filesystem::unlink( pi.path() );
854 }
855 }
856 }
857 } // namespace
859 void RepoManager::Impl::init_knownRepositories()
860 {
861 MIL << "start construct known repos" << endl;
862
863 if ( PathInfo(_options.knownReposPath).isExist() )
864 {
865 std::list<std::string> repoEscAliases;
866 std::list<RepoInfo> orphanedRepos;
867 for ( RepoInfo & repoInfo : repositories_in_dir(_options.knownReposPath) )
868 {
869 // set the metadata path for the repo
870 repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo) );
871 // set the downloaded packages path for the repo
872 repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo) );
873 // remember it
874 _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
875
876 // detect orphaned repos belonging to a deleted service
877 const std::string & serviceAlias( repoInfo.service() );
878 if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
879 {
880 WAR << "Schedule orphaned service repo for deletion: " << repoInfo << endl;
881 orphanedRepos.push_back( repoInfo );
882 continue; // don't remember it in repoEscAliases
883 }
884
885 repoEscAliases.push_back(repoInfo.escaped_alias());
886 }
887
888 // Cleanup orphanded service repos:
889 if ( ! orphanedRepos.empty() )
890 {
891 for ( const auto & repoInfo : orphanedRepos )
892 {
893 MIL << "Delete orphaned service repo " << repoInfo.alias() << endl;
894 // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
895 // %1% = service name
896 // %2% = repository name
897 JobReport::warning( str::Format(_("Unknown service '%1%': Removing orphaned service repository '%2%'"))
898 % repoInfo.service()
899 % repoInfo.alias() );
900 try {
901 removeRepository( repoInfo );
902 }
903 catch ( const Exception & caugth )
904 {
906 }
907 }
908 }
909
910 // bsc#1210740: Don't cleanup if read-only mode was promised.
911 if ( not zypp_readonly_hack::IGotIt() ) {
912 // delete metadata folders without corresponding repo (e.g. old tmp directories)
913 //
914 // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
915 // we'd need somemagic file to identify zypp cache directories. Without this
916 // we may easily remove user data (zypper --pkg-cache-dir . download ...)
917 repoEscAliases.sort();
918 cleanupNonRepoMetadtaFolders( _options.repoRawCachePath,
920 repoEscAliases );
921 cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath,
923 repoEscAliases );
924 // bsc#1204956: Tweak to prevent auto pruning package caches
925 if ( autoPruneInDir( _options.repoPackagesCachePath ) )
926 cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath,
928 repoEscAliases );
929 }
930 }
931 MIL << "end construct known repos" << endl;
932 }
933
935
936 RepoStatus RepoManager::Impl::metadataStatus( const RepoInfo & info ) const
937 {
938 Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
939 Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
940
941 RepoType repokind = info.type();
942 // If unknown, probe the local metadata
943 if ( repokind == RepoType::NONE )
944 repokind = probeCache( productdatapath );
945
946 // NOTE: The calling code expects an empty RepoStatus being returned
947 // if the metadata cache is empty. So additioanl components like the
948 // RepoInfos status are joined after the switch IFF the status is not
949 // empty.
950 RepoStatus status;
951 switch ( repokind.toEnum() )
952 {
953 case RepoType::RPMMD_e :
954 status = RepoStatus( productdatapath/"repodata/repomd.xml");
955 if ( info.requireStatusWithMediaFile() )
956 status = status && RepoStatus( mediarootpath/"media.1/media" );
957 break;
958
959 case RepoType::YAST2_e :
960 status = RepoStatus( productdatapath/"content" ) && RepoStatus( mediarootpath/"media.1/media" );
961 break;
962
964 status = RepoStatus::fromCookieFile( productdatapath/"cookie" ); // dir status at last refresh
965 break;
966
967 case RepoType::NONE_e :
968 // Return default RepoStatus in case of RepoType::NONE
969 // indicating it should be created?
970 // ZYPP_THROW(RepoUnknownTypeException());
971 break;
972 }
973
974 if ( ! status.empty() )
975 status = status && RepoStatus( info );
976
977 return status;
978 }
979
980
981 void RepoManager::Impl::touchIndexFile( const RepoInfo & info )
982 {
983 Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
984
985 RepoType repokind = info.type();
986 if ( repokind.toEnum() == RepoType::NONE_e )
987 // unknown, probe the local metadata
988 repokind = probeCache( productdatapath );
989 // if still unknown, just return
990 if (repokind == RepoType::NONE_e)
991 return;
992
993 Pathname p;
994 switch ( repokind.toEnum() )
995 {
996 case RepoType::RPMMD_e :
997 p = Pathname(productdatapath + "/repodata/repomd.xml");
998 break;
999
1000 case RepoType::YAST2_e :
1001 p = Pathname(productdatapath + "/content");
1002 break;
1003
1005 p = Pathname(productdatapath + "/cookie");
1006 break;
1007
1008 case RepoType::NONE_e :
1009 default:
1010 break;
1011 }
1012
1013 // touch the file, ignore error (they are logged anyway)
1015 }
1016
1017
1018 RepoManager::RefreshCheckStatus RepoManager::Impl::checkIfToRefreshMetadata( const RepoInfo & info, const Url & url, RawMetadataRefreshPolicy policy )
1019 {
1020 assert_alias(info);
1021 try
1022 {
1023 MIL << "Check if to refresh repo " << info.alias() << " at " << url << " (" << info.type() << ")" << endl;
1024
1025 refreshGeoIPData( { url } );
1026
1027 // first check old (cached) metadata
1028 Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
1029 filesystem::assert_dir( mediarootpath );
1030 RepoStatus oldstatus = metadataStatus( info );
1031
1032 if ( oldstatus.empty() )
1033 {
1034 MIL << "No cached metadata, going to refresh" << endl;
1035 return REFRESH_NEEDED;
1036 }
1037
1038 if ( url.schemeIsVolatile() )
1039 {
1040 MIL << "Never refresh CD/DVD" << endl;
1041 return REPO_UP_TO_DATE;
1042 }
1043
1044 if ( policy == RefreshForced )
1045 {
1046 MIL << "Forced refresh!" << endl;
1047 return REFRESH_NEEDED;
1048 }
1049
1050 if ( url.schemeIsLocal() )
1051 {
1052 policy = RefreshIfNeededIgnoreDelay;
1053 }
1054
1055 // Check whether repo.refresh.delay applies...
1056 if ( policy != RefreshIfNeededIgnoreDelay )
1057 {
1058 // bsc#1174016: Prerequisite to skipping the refresh is that metadata
1059 // and solv cache status match. They will not, if the repos URL was
1060 // changed e.g. due to changed repovars.
1061 RepoStatus cachestatus = cacheStatus( info );
1062
1063 if ( oldstatus == cachestatus )
1064 {
1065 // difference in seconds
1066 double diff = ::difftime( (Date::ValueType)Date::now(), (Date::ValueType)oldstatus.timestamp() ) / 60;
1067 if ( diff < ZConfig::instance().repo_refresh_delay() )
1068 {
1069 if ( diff < 0 )
1070 {
1071 WAR << "Repository '" << info.alias() << "' was refreshed in the future!" << endl;
1072 }
1073 else
1074 {
1075 MIL << "Repository '" << info.alias()
1076 << "' has been refreshed less than repo.refresh.delay ("
1078 << ") minutes ago. Advising to skip refresh" << endl;
1079 return REPO_CHECK_DELAYED;
1080 }
1081 }
1082 }
1083 else {
1084 MIL << "Metadata and solv cache don't match. Check data on server..." << endl;
1085 }
1086 }
1087
1088 repo::RepoType repokind = info.type();
1089 // if unknown: probe it
1090 if ( repokind == RepoType::NONE )
1091 repokind = probe( url, info.path() );
1092
1093 // retrieve newstatus
1094 RepoStatus newstatus;
1095 switch ( repokind.toEnum() )
1096 {
1097 case RepoType::RPMMD_e:
1098 {
1099 MediaSetAccess media( url );
1100 newstatus = RepoStatus( info ) && yum::Downloader( info, mediarootpath ).status( media );
1101 }
1102 break;
1103
1104 case RepoType::YAST2_e:
1105 {
1106 MediaSetAccess media( url );
1107 newstatus = RepoStatus( info ) && susetags::Downloader( info, mediarootpath ).status( media );
1108 }
1109 break;
1110
1112 newstatus = RepoStatus( info ) && RepoStatus( MediaMounter(url).getPathName(info.path()) ); // dir status
1113 break;
1114
1115 default:
1116 case RepoType::NONE_e:
1118 break;
1119 }
1120
1121 // check status
1122 if ( oldstatus == newstatus )
1123 {
1124 MIL << "repo has not changed" << endl;
1125 touchIndexFile( info );
1126 return REPO_UP_TO_DATE;
1127 }
1128 else // includes newstatus.empty() if e.g. repo format changed
1129 {
1130 MIL << "repo has changed, going to refresh" << endl;
1131 return REFRESH_NEEDED;
1132 }
1133 }
1134 catch ( const Exception &e )
1135 {
1136 ZYPP_CAUGHT(e);
1137 ERR << "refresh check failed for " << url << endl;
1138 ZYPP_RETHROW(e);
1139 }
1140
1141 return REFRESH_NEEDED; // default
1142 }
1143
1144
1145 void RepoManager::Impl::refreshMetadata( const RepoInfo & info, RawMetadataRefreshPolicy policy, const ProgressData::ReceiverFnc & progress )
1146 {
1147 assert_alias(info);
1148 assert_urls(info);
1149
1150 // make sure geoIP data is up 2 date
1151 refreshGeoIPData( info.baseUrls() );
1152
1153 // we will throw this later if no URL checks out fine
1154 RepoException rexception( info, PL_("Valid metadata not found at specified URL",
1155 "Valid metadata not found at specified URLs",
1156 info.baseUrlsSize() ) );
1157
1158 // Suppress (interactive) media::MediaChangeReport if we in have multiple basurls (>1)
1160 // try urls one by one
1161 for ( RepoInfo::urls_const_iterator it = info.baseUrlsBegin(); it != info.baseUrlsEnd(); ++it )
1162 {
1163 try
1164 {
1165 Url url(*it);
1166
1167 // check whether to refresh metadata
1168 // if the check fails for this url, it throws, so another url will be checked
1169 if (checkIfToRefreshMetadata(info, url, policy)!=REFRESH_NEEDED)
1170 return;
1171
1172 MIL << "Going to refresh metadata from " << url << endl;
1173
1174 // bsc#1048315: Always re-probe in case of repo format change.
1175 // TODO: Would be sufficient to verify the type and re-probe
1176 // if verification failed (or type is RepoType::NONE)
1177 repo::RepoType repokind = info.type();
1178 {
1179 repo::RepoType probed = probe( *it, info.path() );
1180 if ( repokind != probed )
1181 {
1182 repokind = probed;
1183 // update probed type only for repos in system
1184 for_( it, repoBegin(), repoEnd() )
1185 {
1186 if ( info.alias() == (*it).alias() )
1187 {
1188 RepoInfo modifiedrepo = *it;
1189 modifiedrepo.setType( repokind );
1190 // don't modify .repo in refresh.
1191 // modifyRepository( info.alias(), modifiedrepo );
1192 break;
1193 }
1194 }
1195 // Adjust the probed type in RepoInfo
1196 info.setProbedType( repokind ); // lazy init!
1197 }
1198 // no need to continue with an unknown type
1199 if ( repokind.toEnum() == RepoType::NONE_e )
1201 }
1202
1203 Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
1204 if( filesystem::assert_dir(mediarootpath) )
1205 {
1206 Exception ex(str::form( _("Can't create %s"), mediarootpath.c_str()) );
1207 ZYPP_THROW(ex);
1208 }
1209
1210 // create temp dir as sibling of mediarootpath
1211 filesystem::TmpDir tmpdir( filesystem::TmpDir::makeSibling( mediarootpath ) );
1212 if( tmpdir.path().empty() )
1213 {
1214 Exception ex(_("Can't create metadata cache directory."));
1215 ZYPP_THROW(ex);
1216 }
1217
1218 if ( ( repokind.toEnum() == RepoType::RPMMD_e ) ||
1219 ( repokind.toEnum() == RepoType::YAST2_e ) )
1220 {
1221 MediaSetAccess media(url);
1222 shared_ptr<repo::Downloader> downloader_ptr;
1223
1224 MIL << "Creating downloader for [ " << info.alias() << " ]" << endl;
1225
1226 if ( repokind.toEnum() == RepoType::RPMMD_e ) {
1227 downloader_ptr.reset(new yum::Downloader(info, mediarootpath ));
1228 if ( _pluginRepoverification.checkIfNeeded() )
1229 downloader_ptr->setPluginRepoverification( _pluginRepoverification ); // susetags is dead so we apply just to yum
1230 }
1231 else
1232 downloader_ptr.reset( new susetags::Downloader(info, mediarootpath) );
1233
1240 for_( it, repoBegin(), repoEnd() )
1241 {
1242 Pathname cachepath(rawcache_path_for_repoinfo( _options, *it ));
1243 if ( PathInfo(cachepath).isExist() )
1244 downloader_ptr->addCachePath(cachepath);
1245 }
1246
1247 downloader_ptr->download( media, tmpdir.path() );
1248 }
1249 else if ( repokind.toEnum() == RepoType::RPMPLAINDIR_e )
1250 {
1251 // as substitute for real metadata remember the checksum of the directory we refreshed
1252 MediaMounter media( url );
1253 RepoStatus newstatus = RepoStatus( media.getPathName( info.path() ) ); // dir status
1254
1255 Pathname productpath( tmpdir.path() / info.path() );
1256 filesystem::assert_dir( productpath );
1257 newstatus.saveToCookieFile( productpath/"cookie" );
1258 }
1259 else
1260 {
1262 }
1263
1264 // ok we have the metadata, now exchange
1265 // the contents
1266 filesystem::exchange( tmpdir.path(), mediarootpath );
1267 if ( ! isTmpRepo( info ) )
1268 reposManip(); // remember to trigger appdata refresh
1269
1270 // we are done.
1271 return;
1272 }
1273 catch ( const Exception &e )
1274 {
1275 ZYPP_CAUGHT(e);
1276 ERR << "Trying another url..." << endl;
1277
1278 // remember the exception caught for the *first URL*
1279 // if all other URLs fail, the rexception will be thrown with the
1280 // cause of the problem of the first URL remembered
1281 if (it == info.baseUrlsBegin())
1282 rexception.remember(e);
1283 else
1284 rexception.addHistory( e.asUserString() );
1285
1286 }
1287 } // for every url
1288 ERR << "No more urls..." << endl;
1289 ZYPP_THROW(rexception);
1290 }
1291
1293
1294 void RepoManager::Impl::cleanMetadata( const RepoInfo & info, const ProgressData::ReceiverFnc & progressfnc )
1295 {
1296 ProgressData progress(100);
1297 progress.sendTo(progressfnc);
1298 filesystem::recursive_rmdir( ZConfig::instance().geoipCachePath() );
1299 filesystem::recursive_rmdir( rawcache_path_for_repoinfo(_options, info) );
1300 progress.toMax();
1301 }
1302
1303
1304 void RepoManager::Impl::cleanPackages( const RepoInfo & info, const ProgressData::ReceiverFnc & progressfnc, bool isAutoClean_r )
1305 {
1306 ProgressData progress(100);
1307 progress.sendTo(progressfnc);
1308
1309 // bsc#1204956: Tweak to prevent auto pruning package caches
1310 const Pathname & rpc { packagescache_path_for_repoinfo(_options, info) };
1311 if ( not isAutoClean_r || autoPruneInDir( rpc.dirname() ) )
1313 progress.toMax();
1314 }
1315
1316
1317 void RepoManager::Impl::buildCache( const RepoInfo & info, CacheBuildPolicy policy, const ProgressData::ReceiverFnc & progressrcv )
1318 {
1319 assert_alias(info);
1320 Pathname mediarootpath = rawcache_path_for_repoinfo( _options, info );
1321 Pathname productdatapath = rawproductdata_path_for_repoinfo( _options, info );
1322
1323 if( filesystem::assert_dir(_options.repoCachePath) )
1324 {
1325 Exception ex(str::form( _("Can't create %s"), _options.repoCachePath.c_str()) );
1326 ZYPP_THROW(ex);
1327 }
1328 RepoStatus raw_metadata_status = metadataStatus(info);
1329 if ( raw_metadata_status.empty() )
1330 {
1331 /* if there is no cache at this point, we refresh the raw
1332 in case this is the first time - if it's !autorefresh,
1333 we may still refresh */
1334 refreshMetadata(info, RefreshIfNeeded, progressrcv );
1335 raw_metadata_status = metadataStatus(info);
1336 }
1337
1338 bool needs_cleaning = false;
1339 if ( isCached( info ) )
1340 {
1341 MIL << info.alias() << " is already cached." << endl;
1342 RepoStatus cache_status = cacheStatus(info);
1343
1344 if ( cache_status == raw_metadata_status )
1345 {
1346 MIL << info.alias() << " cache is up to date with metadata." << endl;
1347 if ( policy == BuildIfNeeded )
1348 {
1349 // On the fly add missing solv.idx files for bash completion.
1350 const Pathname & base = solv_path_for_repoinfo( _options, info);
1351 if ( ! PathInfo(base/"solv.idx").isExist() )
1352 sat::updateSolvFileIndex( base/"solv" );
1353
1354 return;
1355 }
1356 else {
1357 MIL << info.alias() << " cache rebuild is forced" << endl;
1358 }
1359 }
1360
1361 needs_cleaning = true;
1362 }
1363
1364 ProgressData progress(100);
1366 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1367 progress.name(str::form(_("Building repository '%s' cache"), info.label().c_str()));
1368 progress.toMin();
1369
1370 if (needs_cleaning)
1371 {
1372 cleanCache(info);
1373 }
1374
1375 MIL << info.alias() << " building cache..." << info.type() << endl;
1376
1377 Pathname base = solv_path_for_repoinfo( _options, info);
1378
1379 if( filesystem::assert_dir(base) )
1380 {
1381 Exception ex(str::form( _("Can't create %s"), base.c_str()) );
1382 ZYPP_THROW(ex);
1383 }
1384
1385 if( ! PathInfo(base).userMayW() )
1386 {
1387 Exception ex(str::form( _("Can't create cache at %s - no writing permissions."), base.c_str()) );
1388 ZYPP_THROW(ex);
1389 }
1390 Pathname solvfile = base / "solv";
1391
1392 // do we have type?
1393 repo::RepoType repokind = info.type();
1394
1395 // if the type is unknown, try probing.
1396 switch ( repokind.toEnum() )
1397 {
1398 case RepoType::NONE_e:
1399 // unknown, probe the local metadata
1400 repokind = probeCache( productdatapath );
1401 break;
1402 default:
1403 break;
1404 }
1405
1406 MIL << "repo type is " << repokind << endl;
1407
1408 switch ( repokind.toEnum() )
1409 {
1410 case RepoType::RPMMD_e :
1411 case RepoType::YAST2_e :
1413 {
1414 // Take care we unlink the solvfile on exception
1415 ManagedFile guard( solvfile, filesystem::unlink );
1416 scoped_ptr<MediaMounter> forPlainDirs;
1417
1419 cmd.push_back( PathInfo( "/usr/bin/repo2solv" ).isFile() ? "repo2solv" : "repo2solv.sh" );
1420 // repo2solv expects -o as 1st arg!
1421 cmd.push_back( "-o" );
1422 cmd.push_back( solvfile.asString() );
1423 cmd.push_back( "-X" ); // autogenerate pattern from pattern-package
1424 // bsc#1104415: no more application support // cmd.push_back( "-A" ); // autogenerate application pseudo packages
1425
1426 if ( repokind == RepoType::RPMPLAINDIR )
1427 {
1428 forPlainDirs.reset( new MediaMounter( info.url() ) );
1429 // recusive for plaindir as 2nd arg!
1430 cmd.push_back( "-R" );
1431 // FIXME this does only work form dir: URLs
1432 cmd.push_back( forPlainDirs->getPathName( info.path() ).c_str() );
1433 }
1434 else
1435 cmd.push_back( productdatapath.asString() );
1436
1438 std::string errdetail;
1439
1440 for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
1441 WAR << " " << output;
1442 errdetail += output;
1443 }
1444
1445 int ret = prog.close();
1446 if ( ret != 0 )
1447 {
1448 RepoException ex(info, str::form( _("Failed to cache repo (%d)."), ret ));
1449 ex.addHistory( str::Str() << prog.command() << endl << errdetail << prog.execError() ); // errdetail lines are NL-terminaled!
1450 ZYPP_THROW(ex);
1451 }
1452
1453 // We keep it.
1454 guard.resetDispose();
1455 sat::updateSolvFileIndex( solvfile ); // content digest for zypper bash completion
1456 }
1457 break;
1458 default:
1459 ZYPP_THROW(RepoUnknownTypeException( info, _("Unhandled repository type") ));
1460 break;
1461 }
1462 // update timestamp and checksum
1463 setCacheStatus(info, raw_metadata_status);
1464 MIL << "Commit cache.." << endl;
1465 progress.toMax();
1466 }
1467
1469
1470
1477 repo::RepoType RepoManager::Impl::probe( const Url & url, const Pathname & path ) const
1478 {
1479 MIL << "going to probe the repo type at " << url << " (" << path << ")" << endl;
1480
1481 if ( url.getScheme() == "dir" && ! PathInfo( url.getPathName()/path ).isDir() )
1482 {
1483 // Handle non existing local directory in advance, as
1484 // MediaSetAccess does not support it.
1485 MIL << "Probed type NONE (not exists) at " << url << " (" << path << ")" << endl;
1486 return repo::RepoType::NONE;
1487 }
1488
1489 // prepare exception to be thrown if the type could not be determined
1490 // due to a media exception. We can't throw right away, because of some
1491 // problems with proxy servers returning an incorrect error
1492 // on ftp file-not-found(bnc #335906). Instead we'll check another types
1493 // before throwing.
1494
1495 // TranslatorExplanation '%s' is an URL
1496 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
1497 bool gotMediaException = false;
1498 try
1499 {
1500 MediaSetAccess access(url);
1501 try
1502 {
1503 if ( access.doesFileExist(path/"/repodata/repomd.xml") )
1504 {
1505 MIL << "Probed type RPMMD at " << url << " (" << path << ")" << endl;
1506 return repo::RepoType::RPMMD;
1507 }
1508 }
1509 catch ( const media::MediaException &e )
1510 {
1511 ZYPP_CAUGHT(e);
1512 DBG << "problem checking for repodata/repomd.xml file" << endl;
1513 enew.remember(e);
1514 gotMediaException = true;
1515 }
1516
1517 try
1518 {
1519 if ( access.doesFileExist(path/"/content") )
1520 {
1521 MIL << "Probed type YAST2 at " << url << " (" << path << ")" << endl;
1522 return repo::RepoType::YAST2;
1523 }
1524 }
1525 catch ( const media::MediaException &e )
1526 {
1527 ZYPP_CAUGHT(e);
1528 DBG << "problem checking for content file" << endl;
1529 enew.remember(e);
1530 gotMediaException = true;
1531 }
1532
1533 // if it is a non-downloading URL denoting a directory (bsc#1191286: and no plugin)
1534 if ( ! ( url.schemeIsDownloading() || url.schemeIsPlugin() ) )
1535 {
1536 MediaMounter media( url );
1537 if ( PathInfo(media.getPathName()/path).isDir() )
1538 {
1539 // allow empty dirs for now
1540 MIL << "Probed type RPMPLAINDIR at " << url << " (" << path << ")" << endl;
1542 }
1543 }
1544 }
1545 catch ( const Exception &e )
1546 {
1547 ZYPP_CAUGHT(e);
1548 // TranslatorExplanation '%s' is an URL
1549 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
1550 enew.remember(e);
1551 ZYPP_THROW(enew);
1552 }
1553
1554 if (gotMediaException)
1555 ZYPP_THROW(enew);
1556
1557 MIL << "Probed type NONE at " << url << " (" << path << ")" << endl;
1558 return repo::RepoType::NONE;
1559 }
1560
1566 repo::RepoType RepoManager::Impl::probeCache( const Pathname & path_r ) const
1567 {
1568 MIL << "going to probe the cached repo at " << path_r << endl;
1569
1571
1572 if ( PathInfo(path_r/"/repodata/repomd.xml").isFile() )
1573 { ret = repo::RepoType::RPMMD; }
1574 else if ( PathInfo(path_r/"/content").isFile() )
1575 { ret = repo::RepoType::YAST2; }
1576 else if ( PathInfo(path_r).isDir() )
1578
1579 MIL << "Probed cached type " << ret << " at " << path_r << endl;
1580 return ret;
1581 }
1582
1584
1585 void RepoManager::Impl::cleanCacheDirGarbage( const ProgressData::ReceiverFnc & progressrcv )
1586 {
1587 MIL << "Going to clean up garbage in cache dirs" << endl;
1588
1589 ProgressData progress(300);
1590 progress.sendTo(progressrcv);
1591 progress.toMin();
1592
1593 std::list<Pathname> cachedirs;
1594 cachedirs.push_back(_options.repoRawCachePath);
1595 cachedirs.push_back(_options.repoPackagesCachePath);
1596 cachedirs.push_back(_options.repoSolvCachePath);
1597
1598 for_( dir, cachedirs.begin(), cachedirs.end() )
1599 {
1600 if ( PathInfo(*dir).isExist() )
1601 {
1602 std::list<Pathname> entries;
1603 if ( filesystem::readdir( entries, *dir, false ) != 0 )
1604 // TranslatorExplanation '%s' is a pathname
1605 ZYPP_THROW(Exception(str::form(_("Failed to read directory '%s'"), dir->c_str())));
1606
1607 unsigned sdircount = entries.size();
1608 unsigned sdircurrent = 1;
1609 for_( subdir, entries.begin(), entries.end() )
1610 {
1611 // if it does not belong known repo, make it disappear
1612 bool found = false;
1613 for_( r, repoBegin(), repoEnd() )
1614 if ( subdir->basename() == r->escaped_alias() )
1615 { found = true; break; }
1616
1617 if ( ! found && ( Date::now()-PathInfo(*subdir).mtime() > Date::day ) )
1618 filesystem::recursive_rmdir( *subdir );
1619
1620 progress.set( progress.val() + sdircurrent * 100 / sdircount );
1621 ++sdircurrent;
1622 }
1623 }
1624 else
1625 progress.set( progress.val() + 100 );
1626 }
1627 progress.toMax();
1628 }
1629
1631
1632 void RepoManager::Impl::cleanCache( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
1633 {
1634 ProgressData progress(100);
1635 progress.sendTo(progressrcv);
1636 progress.toMin();
1637
1638 MIL << "Removing raw metadata cache for " << info.alias() << endl;
1639 filesystem::recursive_rmdir(solv_path_for_repoinfo(_options, info));
1640
1641 progress.toMax();
1642 }
1643
1645
1646 void RepoManager::Impl::loadFromCache( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
1647 {
1648 assert_alias(info);
1649 Pathname solvfile = solv_path_for_repoinfo(_options, info) / "solv";
1650
1651 if ( ! PathInfo(solvfile).isExist() )
1653
1655 try
1656 {
1657 Repository repo = sat::Pool::instance().addRepoSolv( solvfile, info );
1658 // test toolversion in order to rebuild solv file in case
1659 // it was written by a different libsolv-tool parser.
1660 const std::string & toolversion( sat::LookupRepoAttr( sat::SolvAttr::repositoryToolVersion, repo ).begin().asString() );
1661 if ( toolversion != LIBSOLV_TOOLVERSION )
1662 {
1663 repo.eraseFromPool();
1664 ZYPP_THROW(Exception(str::Str() << "Solv-file was created by '"<<toolversion<<"'-parser (want "<<LIBSOLV_TOOLVERSION<<")."));
1665 }
1666 }
1667 catch ( const Exception & exp )
1668 {
1669 ZYPP_CAUGHT( exp );
1670 MIL << "Try to handle exception by rebuilding the solv-file" << endl;
1671 cleanCache( info, progressrcv );
1672 buildCache( info, BuildIfNeeded, progressrcv );
1673
1674 sat::Pool::instance().addRepoSolv( solvfile, info );
1675 }
1676 }
1677
1679
1680 void RepoManager::Impl::addRepository( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
1681 {
1682 assert_alias(info);
1683
1684 ProgressData progress(100);
1686 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1687 progress.name(str::form(_("Adding repository '%s'"), info.label().c_str()));
1688 progress.toMin();
1689
1690 MIL << "Try adding repo " << info << endl;
1691
1692 RepoInfo tosave = info;
1693 if ( repos().find(tosave) != repos().end() )
1695
1696 // check the first url for now
1697 if ( _options.probe )
1698 {
1699 DBG << "unknown repository type, probing" << endl;
1700 assert_urls(tosave);
1701
1702 RepoType probedtype( probe( tosave.url(), info.path() ) );
1703 if ( probedtype == RepoType::NONE )
1705 else
1706 tosave.setType(probedtype);
1707 }
1708
1709 progress.set(50);
1710
1711 // assert the directory exists
1712 filesystem::assert_dir(_options.knownReposPath);
1713
1714 Pathname repofile = generateNonExistingName(
1715 _options.knownReposPath, generateFilename(tosave));
1716 // now we have a filename that does not exists
1717 MIL << "Saving repo in " << repofile << endl;
1718
1719 std::ofstream file(repofile.c_str());
1720 if (!file)
1721 {
1722 // TranslatorExplanation '%s' is a filename
1723 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
1724 }
1725
1726 tosave.dumpAsIniOn(file);
1727 tosave.setFilepath(repofile);
1728 tosave.setMetadataPath( rawcache_path_for_repoinfo( _options, tosave ) );
1729 tosave.setPackagesPath( packagescache_path_for_repoinfo( _options, tosave ) );
1730 {
1731 // We should fix the API as we must inject those paths
1732 // into the repoinfo in order to keep it usable.
1733 RepoInfo & oinfo( const_cast<RepoInfo &>(info) );
1734 oinfo.setFilepath(repofile);
1735 oinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, tosave ) );
1736 oinfo.setPackagesPath( packagescache_path_for_repoinfo( _options, tosave ) );
1737 }
1738 reposManip().insert(tosave);
1739
1740 progress.set(90);
1741
1742 // check for credentials in Urls
1743 UrlCredentialExtractor( _options.rootDir ).collect( tosave.baseUrls() );
1744
1745 HistoryLog(_options.rootDir).addRepository(tosave);
1746
1747 progress.toMax();
1748 MIL << "done" << endl;
1749 }
1750
1751
1752 void RepoManager::Impl::addRepositories( const Url & url, const ProgressData::ReceiverFnc & progressrcv )
1753 {
1754 std::list<RepoInfo> repos = readRepoFile(url);
1755 for ( std::list<RepoInfo>::const_iterator it = repos.begin();
1756 it != repos.end();
1757 ++it )
1758 {
1759 // look if the alias is in the known repos.
1760 for_ ( kit, repoBegin(), repoEnd() )
1761 {
1762 if ( (*it).alias() == (*kit).alias() )
1763 {
1764 ERR << "To be added repo " << (*it).alias() << " conflicts with existing repo " << (*kit).alias() << endl;
1766 }
1767 }
1768 }
1769
1770 std::string filename = Pathname(url.getPathName()).basename();
1771
1772 if ( filename == Pathname() )
1773 {
1774 // TranslatorExplanation '%s' is an URL
1775 ZYPP_THROW(RepoException(str::form( _("Invalid repo file name at '%s'"), url.asString().c_str() )));
1776 }
1777
1778 // assert the directory exists
1779 filesystem::assert_dir(_options.knownReposPath);
1780
1781 Pathname repofile = generateNonExistingName(_options.knownReposPath, filename);
1782 // now we have a filename that does not exists
1783 MIL << "Saving " << repos.size() << " repo" << ( repos.size() ? "s" : "" ) << " in " << repofile << endl;
1784
1785 std::ofstream file(repofile.c_str());
1786 if (!file)
1787 {
1788 // TranslatorExplanation '%s' is a filename
1789 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
1790 }
1791
1792 for ( std::list<RepoInfo>::iterator it = repos.begin();
1793 it != repos.end();
1794 ++it )
1795 {
1796 MIL << "Saving " << (*it).alias() << endl;
1797 it->dumpAsIniOn(file);
1798 it->setFilepath(repofile);
1799 it->setMetadataPath( rawcache_path_for_repoinfo( _options, *it ) );
1800 it->setPackagesPath( packagescache_path_for_repoinfo( _options, *it ) );
1801 reposManip().insert(*it);
1802
1803 HistoryLog(_options.rootDir).addRepository(*it);
1804 }
1805
1806 MIL << "done" << endl;
1807 }
1808
1810
1811 void RepoManager::Impl::removeRepository( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
1812 {
1813 ProgressData progress;
1815 progress.sendTo( ProgressReportAdaptor( progressrcv, report ) );
1816 progress.name(str::form(_("Removing repository '%s'"), info.label().c_str()));
1817
1818 MIL << "Going to delete repo " << info.alias() << endl;
1819
1820 for_( it, repoBegin(), repoEnd() )
1821 {
1822 // they can be the same only if the provided is empty, that means
1823 // the provided repo has no alias
1824 // then skip
1825 if ( (!info.alias().empty()) && ( info.alias() != (*it).alias() ) )
1826 continue;
1827
1828 // TODO match by url
1829
1830 // we have a matcing repository, now we need to know
1831 // where it does come from.
1832 RepoInfo todelete = *it;
1833 if (todelete.filepath().empty())
1834 {
1835 ZYPP_THROW(RepoException( todelete, _("Can't figure out where the repo is stored.") ));
1836 }
1837 else
1838 {
1839 // figure how many repos are there in the file:
1840 std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath());
1841 if ( filerepos.size() == 0 // bsc#984494: file may have already been deleted
1842 ||(filerepos.size() == 1 && filerepos.front().alias() == todelete.alias() ) )
1843 {
1844 // easy: file does not exist, contains no or only the repo to delete: delete the file
1845 int ret = filesystem::unlink( todelete.filepath() );
1846 if ( ! ( ret == 0 || ret == ENOENT ) )
1847 {
1848 // TranslatorExplanation '%s' is a filename
1849 ZYPP_THROW(RepoException( todelete, str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
1850 }
1851 MIL << todelete.alias() << " successfully deleted." << endl;
1852 }
1853 else
1854 {
1855 // there are more repos in the same file
1856 // write them back except the deleted one.
1857 //TmpFile tmp;
1858 //std::ofstream file(tmp.path().c_str());
1859
1860 // assert the directory exists
1862
1863 std::ofstream file(todelete.filepath().c_str());
1864 if (!file)
1865 {
1866 // TranslatorExplanation '%s' is a filename
1867 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
1868 }
1869 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1870 fit != filerepos.end();
1871 ++fit )
1872 {
1873 if ( (*fit).alias() != todelete.alias() )
1874 (*fit).dumpAsIniOn(file);
1875 }
1876 }
1877
1878 CombinedProgressData cSubprogrcv(progress, 20);
1879 CombinedProgressData mSubprogrcv(progress, 40);
1880 CombinedProgressData pSubprogrcv(progress, 40);
1881 // now delete it from cache
1882 if ( isCached(todelete) )
1883 cleanCache( todelete, cSubprogrcv);
1884 // now delete metadata (#301037)
1885 cleanMetadata( todelete, mSubprogrcv );
1886 cleanPackages( todelete, pSubprogrcv, true/*isAutoClean*/ );
1887 reposManip().erase(todelete);
1888 MIL << todelete.alias() << " successfully deleted." << endl;
1889 HistoryLog(_options.rootDir).removeRepository(todelete);
1890 return;
1891 } // else filepath is empty
1892
1893 }
1894 // should not be reached on a sucess workflow
1896 }
1897
1899
1900 void RepoManager::Impl::modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, const ProgressData::ReceiverFnc & progressrcv )
1901 {
1902 RepoInfo toedit = getRepositoryInfo(alias);
1903 RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data
1904
1905 // check if the new alias already exists when renaming the repo
1906 if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
1907 {
1909 }
1910
1911 if (toedit.filepath().empty())
1912 {
1913 ZYPP_THROW(RepoException( toedit, _("Can't figure out where the repo is stored.") ));
1914 }
1915 else
1916 {
1917 // figure how many repos are there in the file:
1918 std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath());
1919
1920 // there are more repos in the same file
1921 // write them back except the deleted one.
1922 //TmpFile tmp;
1923 //std::ofstream file(tmp.path().c_str());
1924
1925 // assert the directory exists
1927
1928 std::ofstream file(toedit.filepath().c_str());
1929 if (!file)
1930 {
1931 // TranslatorExplanation '%s' is a filename
1932 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
1933 }
1934 for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
1935 fit != filerepos.end();
1936 ++fit )
1937 {
1938 // if the alias is different, dump the original
1939 // if it is the same, dump the provided one
1940 if ( (*fit).alias() != toedit.alias() )
1941 (*fit).dumpAsIniOn(file);
1942 else
1943 newinfo.dumpAsIniOn(file);
1944 }
1945
1946 if ( toedit.enabled() && !newinfo.enabled() )
1947 {
1948 // On the fly remove solv.idx files for bash completion if a repo gets disabled.
1949 const Pathname & solvidx = solv_path_for_repoinfo(_options, newinfo)/"solv.idx";
1950 if ( PathInfo(solvidx).isExist() )
1951 filesystem::unlink( solvidx );
1952 }
1953
1954 newinfo.setFilepath(toedit.filepath());
1955 newinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, newinfo ) );
1956 newinfo.setPackagesPath( packagescache_path_for_repoinfo( _options, newinfo ) );
1957 {
1958 // We should fix the API as we must inject those paths
1959 // into the repoinfo in order to keep it usable.
1960 RepoInfo & oinfo( const_cast<RepoInfo &>(newinfo_r) );
1961 oinfo.setFilepath(toedit.filepath());
1962 oinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, newinfo ) );
1963 oinfo.setPackagesPath( packagescache_path_for_repoinfo( _options, newinfo ) );
1964 }
1965 reposManip().erase(toedit);
1966 reposManip().insert(newinfo);
1967 // check for credentials in Urls
1968 UrlCredentialExtractor( _options.rootDir ).collect( newinfo.baseUrls() );
1969 HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
1970 MIL << "repo " << alias << " modified" << endl;
1971 }
1972 }
1973
1975
1976 RepoInfo RepoManager::Impl::getRepositoryInfo( const std::string & alias, const ProgressData::ReceiverFnc & progressrcv )
1977 {
1978 RepoConstIterator it( findAlias( alias, repos() ) );
1979 if ( it != repos().end() )
1980 return *it;
1981 RepoInfo info;
1982 info.setAlias( alias );
1984 }
1985
1986
1987 RepoInfo RepoManager::Impl::getRepositoryInfo( const Url & url, const url::ViewOption & urlview, const ProgressData::ReceiverFnc & progressrcv )
1988 {
1989 for_( it, repoBegin(), repoEnd() )
1990 {
1991 for_( urlit, (*it).baseUrlsBegin(), (*it).baseUrlsEnd() )
1992 {
1993 if ( (*urlit).asString(urlview) == url.asString(urlview) )
1994 return *it;
1995 }
1996 }
1997 RepoInfo info;
1998 info.setBaseUrl( url );
2000 }
2001
2003 //
2004 // Services
2005 //
2007
2008 void RepoManager::Impl::addService( const ServiceInfo & service )
2009 {
2010 assert_alias( service );
2011
2012 // check if service already exists
2013 if ( hasService( service.alias() ) )
2015
2016 // Writable ServiceInfo is needed to save the location
2017 // of the .service file. Finaly insert into the service list.
2018 ServiceInfo toSave( service );
2019 saveService( toSave );
2020 _services.insert( toSave );
2021
2022 // check for credentials in Url
2023 UrlCredentialExtractor( _options.rootDir ).collect( toSave.url() );
2024
2025 MIL << "added service " << toSave.alias() << endl;
2026 }
2027
2029
2030 void RepoManager::Impl::removeService( const std::string & alias )
2031 {
2032 MIL << "Going to delete service " << alias << endl;
2033
2034 const ServiceInfo & service = getService( alias );
2035
2036 Pathname location = service.filepath();
2037 if( location.empty() )
2038 {
2039 ZYPP_THROW(ServiceException( service, _("Can't figure out where the service is stored.") ));
2040 }
2041
2042 ServiceSet tmpSet;
2043 parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
2044
2045 // only one service definition in the file
2046 if ( tmpSet.size() == 1 )
2047 {
2048 if ( filesystem::unlink(location) != 0 )
2049 {
2050 // TranslatorExplanation '%s' is a filename
2051 ZYPP_THROW(ServiceException( service, str::form( _("Can't delete '%s'"), location.c_str() ) ));
2052 }
2053 MIL << alias << " successfully deleted." << endl;
2054 }
2055 else
2056 {
2057 filesystem::assert_dir(location.dirname());
2058
2059 std::ofstream file(location.c_str());
2060 if( !file )
2061 {
2062 // TranslatorExplanation '%s' is a filename
2063 ZYPP_THROW( Exception(str::form( _("Can't open file '%s' for writing."), location.c_str() )));
2064 }
2065
2066 for_(it, tmpSet.begin(), tmpSet.end())
2067 {
2068 if( it->alias() != alias )
2069 it->dumpAsIniOn(file);
2070 }
2071
2072 MIL << alias << " successfully deleted from file " << location << endl;
2073 }
2074
2075 // now remove all repositories added by this service
2076 RepoCollector rcollector;
2077 getRepositoriesInService( alias,
2078 boost::make_function_output_iterator( bind( &RepoCollector::collect, &rcollector, _1 ) ) );
2079 // cannot do this directly in getRepositoriesInService - would invalidate iterators
2080 for_(rit, rcollector.repos.begin(), rcollector.repos.end())
2081 removeRepository(*rit);
2082 }
2083
2085
2086 void RepoManager::Impl::refreshServices( const RefreshServiceOptions & options_r )
2087 {
2088 // copy the set of services since refreshService
2089 // can eventually invalidate the iterator
2090 ServiceSet services( serviceBegin(), serviceEnd() );
2091 for_( it, services.begin(), services.end() )
2092 {
2093 if ( !it->enabled() )
2094 continue;
2095
2096 try {
2097 refreshService(*it, options_r);
2098 }
2099 catch ( const repo::ServicePluginInformalException & e )
2100 { ;/* ignore ServicePluginInformalException */ }
2101 }
2102 }
2103
2104 void RepoManager::Impl::refreshService( const std::string & alias, const RefreshServiceOptions & options_r )
2105 {
2106 ServiceInfo service( getService( alias ) );
2107 assert_alias( service );
2108 assert_url( service );
2109 MIL << "Going to refresh service '" << service.alias() << "', url: " << service.url() << ", opts: " << options_r << endl;
2110
2111 if ( service.ttl() && !( options_r.testFlag( RefreshService_forceRefresh) || options_r.testFlag( RefreshService_restoreStatus ) ) )
2112 {
2113 // Service defines a TTL; maybe we can re-use existing data without refresh.
2114 Date lrf = service.lrf();
2115 if ( lrf )
2116 {
2117 Date now( Date::now() );
2118 if ( lrf <= now )
2119 {
2120 if ( (lrf+=service.ttl()) > now ) // lrf+= !
2121 {
2122 MIL << "Skip: '" << service.alias() << "' metadata valid until " << lrf << endl;
2123 return;
2124 }
2125 }
2126 else
2127 WAR << "Force: '" << service.alias() << "' metadata last refresh in the future: " << lrf << endl;
2128 }
2129 }
2130
2131 // NOTE: It might be necessary to modify and rewrite the service info.
2132 // Either when probing the type, or when adjusting the repositories
2133 // enable/disable state.:
2134 bool serviceModified = false;
2135
2137
2138 // if the type is unknown, try probing.
2139 if ( service.type() == repo::ServiceType::NONE )
2140 {
2141 repo::ServiceType type = probeService( service.url() );
2142 if ( type != ServiceType::NONE )
2143 {
2144 service.setProbedType( type ); // lazy init!
2145 serviceModified = true;
2146 }
2147 }
2148
2149 // get target distro identifier
2150 std::string servicesTargetDistro = _options.servicesTargetDistro;
2151 if ( servicesTargetDistro.empty() )
2152 {
2153 servicesTargetDistro = Target::targetDistribution( Pathname() );
2154 }
2155 DBG << "ServicesTargetDistro: " << servicesTargetDistro << endl;
2156
2157 // parse it
2158 Date::Duration origTtl = service.ttl(); // FIXME Ugly hack: const service.ttl modified when parsing
2159 RepoCollector collector(servicesTargetDistro);
2160 // FIXME Ugly hack: ServiceRepos may throw ServicePluginInformalException
2161 // which is actually a notification. Using an exception for this
2162 // instead of signal/callback is bad. Needs to be fixed here, in refreshServices()
2163 // and in zypper.
2164 std::pair<DefaultIntegral<bool,false>, repo::ServicePluginInformalException> uglyHack;
2165 try {
2166 // FIXME bsc#1080693: Shortcoming of (plugin)services (and repos as well) is that they
2167 // are not aware of the RepoManagers rootDir. The service url, as created in known_services,
2168 // contains the full path to the script. The script however has to be executed chrooted.
2169 // Repos would need to know the RepoMangers rootDir to use the correct vars.d to replace
2170 // repos variables. Until RepoInfoBase is aware if the rootDir, we need to explicitly pass it
2171 // to ServiceRepos.
2172 ServiceRepos( _options.rootDir, service, bind( &RepoCollector::collect, &collector, _1 ) );
2173 }
2174 catch ( const repo::ServicePluginInformalException & e )
2175 {
2176 /* ignore ServicePluginInformalException and throw later */
2177 uglyHack.first = true;
2178 uglyHack.second = e;
2179 }
2180 if ( service.ttl() != origTtl ) // repoindex.xml changed ttl
2181 {
2182 if ( !service.ttl() )
2183 service.setLrf( Date() ); // don't need lrf when zero ttl
2184 serviceModified = true;
2185 }
2187 // On the fly remember the new repo states as defined the reopoindex.xml.
2188 // Move into ServiceInfo later.
2189 ServiceInfo::RepoStates newRepoStates;
2190
2191 // set service alias and base url for all collected repositories
2192 for_( it, collector.repos.begin(), collector.repos.end() )
2193 {
2194 // First of all: Prepend service alias:
2195 it->setAlias( str::form( "%s:%s", service.alias().c_str(), it->alias().c_str() ) );
2196 // set reference to the parent service
2197 it->setService( service.alias() );
2198
2199 // remember the new parsed repo state
2200 newRepoStates[it->alias()] = *it;
2201
2202 // - If the repo url was not set by the repoindex parser, set service's url.
2203 // - Libzypp currently has problem with separate url + path handling so just
2204 // append a path, if set, to the baseurls
2205 // - Credentials in the url authority will be extracted later, either if the
2206 // repository is added or if we check for changed urls.
2207 Pathname path;
2208 if ( !it->path().empty() )
2209 {
2210 if ( it->path() != "/" )
2211 path = it->path();
2212 it->setPath("");
2213 }
2214
2215 if ( it->baseUrlsEmpty() )
2216 {
2217 Url url( service.rawUrl() );
2218 if ( !path.empty() )
2219 url.setPathName( url.getPathName() / path );
2220 it->setBaseUrl( std::move(url) );
2221 }
2222 else if ( !path.empty() )
2223 {
2224 RepoInfo::url_set urls( it->rawBaseUrls() );
2225 for ( Url & url : urls )
2226 {
2227 url.setPathName( url.getPathName() / path );
2228 }
2229 it->setBaseUrls( std::move(urls) );
2230 }
2231 }
2232
2234 // Now compare collected repos with the ones in the system...
2235 //
2236 RepoInfoList oldRepos;
2237 getRepositoriesInService( service.alias(), std::back_inserter( oldRepos ) );
2238
2240 // find old repositories to remove...
2241 for_( oldRepo, oldRepos.begin(), oldRepos.end() )
2242 {
2243 if ( ! foundAliasIn( oldRepo->alias(), collector.repos ) )
2244 {
2245 if ( oldRepo->enabled() )
2246 {
2247 // Currently enabled. If this was a user modification remember the state.
2248 const auto & last = service.repoStates().find( oldRepo->alias() );
2249 if ( last != service.repoStates().end() && ! last->second.enabled )
2250 {
2251 DBG << "Service removes user enabled repo " << oldRepo->alias() << endl;
2252 service.addRepoToEnable( oldRepo->alias() );
2253 serviceModified = true;
2254 }
2255 else
2256 DBG << "Service removes enabled repo " << oldRepo->alias() << endl;
2257 }
2258 else
2259 DBG << "Service removes disabled repo " << oldRepo->alias() << endl;
2260
2261 removeRepository( *oldRepo );
2262 }
2263 }
2264
2266 // create missing repositories and modify existing ones if needed...
2267 UrlCredentialExtractor urlCredentialExtractor( _options.rootDir ); // To collect any credentials stored in repo URLs
2268 for_( it, collector.repos.begin(), collector.repos.end() )
2269 {
2270 // User explicitly requested the repo being enabled?
2271 // User explicitly requested the repo being disabled?
2272 // And hopefully not both ;) If so, enable wins.
2273
2274 TriBool toBeEnabled( indeterminate ); // indeterminate - follow the service request
2275 DBG << "Service request to " << (it->enabled()?"enable":"disable") << " service repo " << it->alias() << endl;
2276
2277 if ( options_r.testFlag( RefreshService_restoreStatus ) )
2278 {
2279 DBG << "Opt RefreshService_restoreStatus " << it->alias() << endl;
2280 // this overrides any pending request!
2281 // Remove from enable request list.
2282 // NOTE: repoToDisable is handled differently.
2283 // It gets cleared on each refresh.
2284 service.delRepoToEnable( it->alias() );
2285 // toBeEnabled stays indeterminate!
2286 }
2287 else
2288 {
2289 if ( service.repoToEnableFind( it->alias() ) )
2290 {
2291 DBG << "User request to enable service repo " << it->alias() << endl;
2292 toBeEnabled = true;
2293 // Remove from enable request list.
2294 // NOTE: repoToDisable is handled differently.
2295 // It gets cleared on each refresh.
2296 service.delRepoToEnable( it->alias() );
2297 serviceModified = true;
2298 }
2299 else if ( service.repoToDisableFind( it->alias() ) )
2300 {
2301 DBG << "User request to disable service repo " << it->alias() << endl;
2302 toBeEnabled = false;
2303 }
2304 }
2305
2306 RepoInfoList::iterator oldRepo( findAlias( it->alias(), oldRepos ) );
2307 if ( oldRepo == oldRepos.end() )
2308 {
2309 // Not found in oldRepos ==> a new repo to add
2310
2311 // Make sure the service repo is created with the appropriate enablement
2312 if ( ! indeterminate(toBeEnabled) )
2313 it->setEnabled( ( bool ) toBeEnabled );
2314
2315 DBG << "Service adds repo " << it->alias() << " " << (it->enabled()?"enabled":"disabled") << endl;
2316 addRepository( *it );
2317 }
2318 else
2319 {
2320 // ==> an exising repo to check
2321 bool oldRepoModified = false;
2322
2323 if ( indeterminate(toBeEnabled) )
2324 {
2325 // No user request: check for an old user modificaton otherwise follow service request.
2326 // NOTE: Assert toBeEnabled is boolean afterwards!
2327 if ( oldRepo->enabled() == it->enabled() )
2328 toBeEnabled = it->enabled(); // service requests no change to the system
2329 else if (options_r.testFlag( RefreshService_restoreStatus ) )
2330 {
2331 toBeEnabled = it->enabled(); // RefreshService_restoreStatus forced
2332 DBG << "Opt RefreshService_restoreStatus " << it->alias() << " forces " << (toBeEnabled?"enabled":"disabled") << endl;
2333 }
2334 else
2335 {
2336 const auto & last = service.repoStates().find( oldRepo->alias() );
2337 if ( last == service.repoStates().end() || last->second.enabled != it->enabled() )
2338 toBeEnabled = it->enabled(); // service request has changed since last refresh -> follow
2339 else
2340 {
2341 toBeEnabled = oldRepo->enabled(); // service request unchaned since last refresh -> keep user modification
2342 DBG << "User modified service repo " << it->alias() << " may stay " << (toBeEnabled?"enabled":"disabled") << endl;
2343 }
2344 }
2345 }
2346
2347 // changed enable?
2348 if ( toBeEnabled == oldRepo->enabled() )
2349 {
2350 DBG << "Service repo " << it->alias() << " stays " << (oldRepo->enabled()?"enabled":"disabled") << endl;
2351 }
2352 else if ( toBeEnabled )
2353 {
2354 DBG << "Service repo " << it->alias() << " gets enabled" << endl;
2355 oldRepo->setEnabled( true );
2356 oldRepoModified = true;
2357 }
2358 else
2359 {
2360 DBG << "Service repo " << it->alias() << " gets disabled" << endl;
2361 oldRepo->setEnabled( false );
2362 oldRepoModified = true;
2363 }
2364
2365 // all other attributes follow the service request:
2366
2367 // changed name (raw!)
2368 if ( oldRepo->rawName() != it->rawName() )
2369 {
2370 DBG << "Service repo " << it->alias() << " gets new NAME " << it->rawName() << endl;
2371 oldRepo->setName( it->rawName() );
2372 oldRepoModified = true;
2373 }
2374
2375 // changed autorefresh
2376 if ( oldRepo->autorefresh() != it->autorefresh() )
2377 {
2378 DBG << "Service repo " << it->alias() << " gets new AUTOREFRESH " << it->autorefresh() << endl;
2379 oldRepo->setAutorefresh( it->autorefresh() );
2380 oldRepoModified = true;
2381 }
2382
2383 // changed priority?
2384 if ( oldRepo->priority() != it->priority() )
2385 {
2386 DBG << "Service repo " << it->alias() << " gets new PRIORITY " << it->priority() << endl;
2387 oldRepo->setPriority( it->priority() );
2388 oldRepoModified = true;
2389 }
2390
2391 // changed url?
2392 {
2393 RepoInfo::url_set newUrls( it->rawBaseUrls() );
2394 urlCredentialExtractor.extract( newUrls ); // Extract! to prevent passwds from disturbing the comparison below
2395 if ( oldRepo->rawBaseUrls() != newUrls )
2396 {
2397 DBG << "Service repo " << it->alias() << " gets new URLs " << newUrls << endl;
2398 oldRepo->setBaseUrls( std::move(newUrls) );
2399 oldRepoModified = true;
2400 }
2401 }
2402
2403 // changed gpg check settings?
2404 // ATM only plugin services can set GPG values.
2405 if ( service.type() == ServiceType::PLUGIN )
2406 {
2407 TriBool ogpg[3]; // Gpg RepoGpg PkgGpg
2408 TriBool ngpg[3];
2409 oldRepo->getRawGpgChecks( ogpg[0], ogpg[1], ogpg[2] );
2410 it-> getRawGpgChecks( ngpg[0], ngpg[1], ngpg[2] );
2411#define Z_CHKGPG(I,N) \
2412 if ( ! sameTriboolState( ogpg[I], ngpg[I] ) ) \
2413 { \
2414 DBG << "Service repo " << it->alias() << " gets new "#N"Check " << ngpg[I] << endl; \
2415 oldRepo->set##N##Check( ngpg[I] ); \
2416 oldRepoModified = true; \
2417 }
2418 Z_CHKGPG( 0, Gpg );
2419 Z_CHKGPG( 1, RepoGpg );
2420 Z_CHKGPG( 2, PkgGpg );
2421#undef Z_CHKGPG
2422 }
2423
2424 // save if modified:
2425 if ( oldRepoModified )
2426 {
2427 modifyRepository( oldRepo->alias(), *oldRepo );
2428 }
2429 }
2430 }
2431
2432 // Unlike reposToEnable, reposToDisable is always cleared after refresh.
2433 if ( ! service.reposToDisableEmpty() )
2434 {
2435 service.clearReposToDisable();
2436 serviceModified = true;
2437 }
2438
2439 // Remember original service request for next refresh
2440 if ( service.repoStates() != newRepoStates )
2441 {
2442 service.setRepoStates( std::move(newRepoStates) );
2443 serviceModified = true;
2444 }
2445
2447 // save service if modified: (unless a plugin service)
2448 if ( service.type() != ServiceType::PLUGIN )
2449 {
2450 if ( service.ttl() )
2451 {
2452 service.setLrf( Date::now() ); // remember last refresh
2453 serviceModified = true; // or use a cookie file
2454 }
2455
2456 if ( serviceModified )
2457 {
2458 // write out modified service file.
2459 modifyService( service.alias(), service );
2460 }
2461 }
2462
2463 if ( uglyHack.first )
2464 {
2465 throw( uglyHack.second ); // intentionally not ZYPP_THROW
2466 }
2467 }
2468
2470
2471 void RepoManager::Impl::modifyService( const std::string & oldAlias, const ServiceInfo & newService )
2472 {
2473 MIL << "Going to modify service " << oldAlias << endl;
2474
2475 // we need a writable copy to link it to the file where
2476 // it is saved if we modify it
2477 ServiceInfo service(newService);
2478
2479 if ( service.type() == ServiceType::PLUGIN )
2480 {
2482 }
2483
2484 const ServiceInfo & oldService = getService(oldAlias);
2485
2486 Pathname location = oldService.filepath();
2487 if( location.empty() )
2488 {
2489 ZYPP_THROW(ServiceException( oldService, _("Can't figure out where the service is stored.") ));
2490 }
2491
2492 // remember: there may multiple services being defined in one file:
2493 ServiceSet tmpSet;
2494 parser::ServiceFileReader( location, ServiceCollector(tmpSet) );
2495
2496 filesystem::assert_dir(location.dirname());
2497 std::ofstream file(location.c_str());
2498 for_(it, tmpSet.begin(), tmpSet.end())
2499 {
2500 if( *it != oldAlias )
2501 it->dumpAsIniOn(file);
2502 }
2503 service.dumpAsIniOn(file);
2504 file.close();
2505 service.setFilepath(location);
2506
2507 _services.erase(oldAlias);
2508 _services.insert(service);
2509 // check for credentials in Urls
2510 UrlCredentialExtractor( _options.rootDir ).collect( service.url() );
2511
2512
2513 // changed properties affecting also repositories
2514 if ( oldAlias != service.alias() // changed alias
2515 || oldService.enabled() != service.enabled() ) // changed enabled status
2516 {
2517 std::vector<RepoInfo> toModify;
2518 getRepositoriesInService(oldAlias, std::back_inserter(toModify));
2519 for_( it, toModify.begin(), toModify.end() )
2520 {
2521 if ( oldService.enabled() != service.enabled() )
2522 {
2523 if ( service.enabled() )
2524 {
2525 // reset to last refreshs state
2526 const auto & last = service.repoStates().find( it->alias() );
2527 if ( last != service.repoStates().end() )
2528 it->setEnabled( last->second.enabled );
2529 }
2530 else
2531 it->setEnabled( false );
2532 }
2533
2534 if ( oldAlias != service.alias() )
2535 it->setService(service.alias());
2536
2537 modifyRepository(it->alias(), *it);
2538 }
2539 }
2540
2542 }
2543
2545
2546 repo::ServiceType RepoManager::Impl::probeService( const Url & url ) const
2547 {
2548 try
2549 {
2550 MediaSetAccess access(url);
2551 if ( access.doesFileExist("/repo/repoindex.xml") )
2553 }
2554 catch ( const media::MediaException &e )
2555 {
2556 ZYPP_CAUGHT(e);
2557 // TranslatorExplanation '%s' is an URL
2558 RepoException enew(str::form( _("Error trying to read from '%s'"), url.asString().c_str() ));
2559 enew.remember(e);
2560 ZYPP_THROW(enew);
2561 }
2562 catch ( const Exception &e )
2563 {
2564 ZYPP_CAUGHT(e);
2565 // TranslatorExplanation '%s' is an URL
2566 Exception enew(str::form( _("Unknown error reading from '%s'"), url.asString().c_str() ));
2567 enew.remember(e);
2568 ZYPP_THROW(enew);
2569 }
2570
2572 }
2573
2574 void RepoManager::Impl::refreshGeoIPData ( const RepoInfo::url_set &urls )
2575 {
2576 try {
2577
2578 if ( !ZConfig::instance().geoipEnabled() ) {
2579 MIL << "GeoIp disabled via ZConfig, not refreshing the GeoIP information." << std::endl;
2580 return;
2581 }
2582
2583 std::vector<std::string> hosts;
2584 for ( const auto &baseUrl : urls ) {
2585 const auto &host = baseUrl.getHost();
2586 if ( zypp::any_of( ZConfig::instance().geoipHostnames(), [&host]( const auto &elem ){ return ( zypp::str::compareCI( host, elem ) == 0 ); } ) ) {
2587 hosts.push_back( host );
2588 break;
2589 }
2590 }
2591
2592 if ( hosts.empty() ) {
2593 MIL << "No configured geoip URL found, not updating geoip data" << std::endl;
2594 return;
2595 }
2596
2597 const auto &geoIPCache = ZConfig::instance().geoipCachePath();
2598
2599 if ( filesystem::assert_dir( geoIPCache ) != 0 ) {
2600 MIL << "Unable to create cache directory for GeoIP." << std::endl;
2601 return;
2602 }
2603
2604 if ( !PathInfo(geoIPCache).userMayRWX() ) {
2605 MIL << "No access rights for the GeoIP cache directory." << std::endl;
2606 return;
2607 }
2608
2609 // remove all older cache entries
2610 filesystem::dirForEachExt( geoIPCache, []( const Pathname &dir, const filesystem::DirEntry &entry ){
2611 if ( entry.type != filesystem::FT_FILE )
2612 return true;
2613
2614 PathInfo pi( dir/entry.name );
2615 auto age = std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t( pi.mtime() );
2616 if ( age < std::chrono::hours(24) )
2617 return true;
2618
2619 MIL << "Removing GeoIP file for " << entry.name << " since it's older than 24hrs." << std::endl;
2620 filesystem::unlink( dir/entry.name );
2621 return true;
2622 });
2623
2624 // go over all found hostnames
2625 std::for_each( hosts.begin(), hosts.end(), [ & ]( const std::string &hostname ) {
2626
2627 // do not query files that are still there
2628 if ( zypp::PathInfo( geoIPCache / hostname ).isExist() ) {
2629 MIL << "Skipping GeoIP request for " << hostname << " since a valid cache entry exists." << std::endl;
2630 return;
2631 }
2632
2633 MIL << "Query GeoIP for " << hostname << std::endl;
2634
2635 zypp::Url url;
2636 try
2637 {
2638 url.setHost(hostname);
2639 url.setScheme("https");
2640 }
2641 catch(const zypp::Exception &e )
2642 {
2643 ZYPP_CAUGHT(e);
2644 MIL << "Ignoring invalid GeoIP hostname: " << hostname << std::endl;
2645 return;
2646 }
2647
2648 MediaSetAccess acc( url );
2649 zypp::ManagedFile file;
2650 try {
2651 // query the file from the server
2652 file = zypp::ManagedFile (acc.provideOptionalFile("/geoip"), filesystem::unlink );
2653
2654 } catch ( const zypp::Exception &e ) {
2655 ZYPP_CAUGHT(e);
2656 MIL << "Failed to query GeoIP from hostname: " << hostname << std::endl;
2657 return;
2658 }
2659 if ( !file->empty() ) {
2660
2661 constexpr auto writeHostToFile = []( const Pathname &fName, const std::string &host ){
2662 std::ofstream out;
2663 out.open( fName.asString(), std::ios_base::trunc );
2664 if ( out.is_open() ) {
2665 out << host << std::endl;
2666 } else {
2667 MIL << "Failed to create/open GeoIP cache file " << fName << std::endl;
2668 }
2669 };
2670
2671 std::string geoipMirror;
2672 try {
2673 xml::Reader reader( *file );
2674 if ( reader.seekToNode( 1, "host" ) ) {
2675 const auto &str = reader.nodeText().asString();
2676
2677 // make a dummy URL to ensure the hostname is valid
2678 zypp::Url testUrl;
2679 testUrl.setHost(str);
2680 testUrl.setScheme("https");
2681
2682 if ( testUrl.isValid() ) {
2683 MIL << "Storing geoIP redirection: " << hostname << " -> " << str << std::endl;
2684 geoipMirror = str;
2685 }
2686
2687 } else {
2688 MIL << "No host entry or empty file returned for GeoIP, remembering for 24hrs" << std::endl;
2689 }
2690 } catch ( const zypp::Exception &e ) {
2691 ZYPP_CAUGHT(e);
2692 MIL << "Empty or invalid GeoIP file, not requesting again for 24hrs" << std::endl;
2693 }
2694
2695 writeHostToFile( geoIPCache / hostname, geoipMirror );
2696 }
2697 });
2698
2699 } catch ( const zypp::Exception &e ) {
2700 ZYPP_CAUGHT(e);
2701 MIL << "Failed to query GeoIP data." << std::endl;
2702 }
2703 }
2704
2706 //
2707 // CLASS NAME : RepoManager
2708 //
2710
2712 : _pimpl( new Impl(opt) )
2713 {}
2714
2716 {}
2717
2718 bool RepoManager::repoEmpty() const
2719 { return _pimpl->repoEmpty(); }
2720
2722 { return _pimpl->repoSize(); }
2723
2725 { return _pimpl->repoBegin(); }
2726
2728 { return _pimpl->repoEnd(); }
2729
2730 RepoInfo RepoManager::getRepo( const std::string & alias ) const
2731 { return _pimpl->getRepo( alias ); }
2732
2733 bool RepoManager::hasRepo( const std::string & alias ) const
2734 { return _pimpl->hasRepo( alias ); }
2735
2736 std::string RepoManager::makeStupidAlias( const Url & url_r )
2737 {
2738 std::string ret( url_r.getScheme() );
2739 if ( ret.empty() )
2740 ret = "repo-";
2741 else
2742 ret += "-";
2743
2744 std::string host( url_r.getHost() );
2745 if ( ! host.empty() )
2746 {
2747 ret += host;
2748 ret += "-";
2749 }
2750
2751 static Date::ValueType serial = Date::now();
2752 ret += Digest::digest( Digest::sha1(), str::hexstring( ++serial ) +url_r.asCompleteString() ).substr(0,8);
2753 return ret;
2754 }
2755
2757 { return _pimpl->metadataStatus( info ); }
2758
2760 { return _pimpl->checkIfToRefreshMetadata( info, url, policy ); }
2761
2762 Pathname RepoManager::metadataPath( const RepoInfo &info ) const
2763 { return _pimpl->metadataPath( info ); }
2764
2765 Pathname RepoManager::packagesPath( const RepoInfo &info ) const
2766 { return _pimpl->packagesPath( info ); }
2767
2769 { return _pimpl->refreshMetadata( info, policy, progressrcv ); }
2770
2771 void RepoManager::cleanMetadata( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2772 { return _pimpl->cleanMetadata( info, progressrcv ); }
2773
2774 void RepoManager::cleanPackages( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2775 { return _pimpl->cleanPackages( info, progressrcv ); }
2776
2777 RepoStatus RepoManager::cacheStatus( const RepoInfo &info ) const
2778 { return _pimpl->cacheStatus( info ); }
2779
2780 void RepoManager::buildCache( const RepoInfo &info, CacheBuildPolicy policy, const ProgressData::ReceiverFnc & progressrcv )
2781 { return _pimpl->buildCache( info, policy, progressrcv ); }
2782
2783 void RepoManager::cleanCache( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2784 { return _pimpl->cleanCache( info, progressrcv ); }
2785
2786 bool RepoManager::isCached( const RepoInfo &info ) const
2787 { return _pimpl->isCached( info ); }
2788
2789 void RepoManager::loadFromCache( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2790 { return _pimpl->loadFromCache( info, progressrcv ); }
2791
2793 { return _pimpl->cleanCacheDirGarbage( progressrcv ); }
2794
2795 repo::RepoType RepoManager::probe( const Url & url, const Pathname & path ) const
2796 { return _pimpl->probe( url, path ); }
2797
2799 { return _pimpl->probe( url ); }
2800
2801 void RepoManager::addRepository( const RepoInfo &info, const ProgressData::ReceiverFnc & progressrcv )
2802 { return _pimpl->addRepository( info, progressrcv ); }
2803
2804 void RepoManager::addRepositories( const Url &url, const ProgressData::ReceiverFnc & progressrcv )
2805 { return _pimpl->addRepositories( url, progressrcv ); }
2806
2807 void RepoManager::removeRepository( const RepoInfo & info, const ProgressData::ReceiverFnc & progressrcv )
2808 { return _pimpl->removeRepository( info, progressrcv ); }
2809
2810 void RepoManager::modifyRepository( const std::string &alias, const RepoInfo & newinfo, const ProgressData::ReceiverFnc & progressrcv )
2811 { return _pimpl->modifyRepository( alias, newinfo, progressrcv ); }
2812
2813 RepoInfo RepoManager::getRepositoryInfo( const std::string &alias, const ProgressData::ReceiverFnc & progressrcv )
2814 { return _pimpl->getRepositoryInfo( alias, progressrcv ); }
2815
2816 RepoInfo RepoManager::getRepositoryInfo( const Url & url, const url::ViewOption & urlview, const ProgressData::ReceiverFnc & progressrcv )
2817 { return _pimpl->getRepositoryInfo( url, urlview, progressrcv ); }
2818
2819 bool RepoManager::serviceEmpty() const
2820 { return _pimpl->serviceEmpty(); }
2821
2823 { return _pimpl->serviceSize(); }
2824
2826 { return _pimpl->serviceBegin(); }
2827
2829 { return _pimpl->serviceEnd(); }
2830
2831 ServiceInfo RepoManager::getService( const std::string & alias ) const
2832 { return _pimpl->getService( alias ); }
2833
2834 bool RepoManager::hasService( const std::string & alias ) const
2835 { return _pimpl->hasService( alias ); }
2836
2838 { return _pimpl->probeService( url ); }
2839
2840 void RepoManager::addService( const std::string & alias, const Url& url )
2841 { return _pimpl->addService( alias, url ); }
2842
2843 void RepoManager::addService( const ServiceInfo & service )
2844 { return _pimpl->addService( service ); }
2845
2846 void RepoManager::removeService( const std::string & alias )
2847 { return _pimpl->removeService( alias ); }
2848
2849 void RepoManager::removeService( const ServiceInfo & service )
2850 { return _pimpl->removeService( service ); }
2851
2853 { return _pimpl->refreshServices( options_r ); }
2854
2855 void RepoManager::refreshService( const std::string & alias, const RefreshServiceOptions & options_r )
2856 { return _pimpl->refreshService( alias, options_r ); }
2857
2858 void RepoManager::refreshService( const ServiceInfo & service, const RefreshServiceOptions & options_r )
2859 { return _pimpl->refreshService( service, options_r ); }
2860
2861 void RepoManager::modifyService( const std::string & oldAlias, const ServiceInfo & service )
2862 { return _pimpl->modifyService( oldAlias, service ); }
2863
2865 { return _pimpl->refreshGeoIPData( urls ); }
2866
2868
2869 std::ostream & operator<<( std::ostream & str, const RepoManager & obj )
2870 { return str << *obj._pimpl; }
2871
2873} // 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:739
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:236
static const std::string & sha1()
sha1
Definition: Digest.cc:44
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:228
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:56
static RepoManagerOptions makeTestSetup(const Pathname &root_r)
Test setup adjusting all paths to be located below one root_r directory.
Definition: RepoManager.cc:500
Pathname rootDir
remembers root_r value for later use
Definition: RepoManager.h:98
Pathname repoPackagesCachePath
Definition: RepoManager.h:84
RepoManagerOptions(const Pathname &root_r=Pathname())
Default ctor following ZConfig global settings.
Definition: RepoManager.cc:486
Functor thats filter RepoInfo by service which it belongs to.
Definition: RepoManager.h:650
creates and provides information about known sources.
Definition: RepoManager.cc:535
bool hasRepo(const std::string &alias) const
Definition: RepoManager.cc:595
ServiceSet::const_iterator ServiceConstIterator
Definition: RepoManager.h:117
bool serviceEmpty() const
Definition: RepoManager.cc:650
bool hasService(const std::string &alias) const
Definition: RepoManager.cc:655
RefreshCheckStatus
Possibly return state of checkIfRefreshMEtadata function.
Definition: RepoManager.h:198
RefreshCheckStatus checkIfToRefreshMetadata(const RepoInfo &info, const Url &url, RawMetadataRefreshPolicy policy)
Impl * clone() const
clone for RWCOW_pointer
Definition: RepoManager.cc:733
void refreshService(const ServiceInfo &service, const RefreshServiceOptions &options_r)
Definition: RepoManager.cc:676
void addRepository(const RepoInfo &info, OPT_PROGRESS)
RepoSet::const_iterator RepoConstIterator
Definition: RepoManager.h:122
RepoInfo getRepositoryInfo(const Url &url, const url::ViewOption &urlview, OPT_PROGRESS)
void addService(const std::string &alias, const Url &url)
Definition: RepoManager.cc:666
std::string generateFilename(const ServiceInfo &info) const
Definition: RepoManager.cc:693
bool isCached(const RepoInfo &info) const
Definition: RepoManager.cc:630
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:598
repo::ServiceType probeService(const Url &url) const
RWCOW_pointer< Impl > _pimpl
Pointer to implementation.
Definition: RepoManager.h:706
RepoInfo getRepositoryInfo(const std::string &alias, OPT_PROGRESS)
void cleanCacheDirGarbage(OPT_PROGRESS)
RepoSet & reposManip()
Definition: RepoManager.cc:719
void addService(const ServiceInfo &service)
bool repoEmpty() const
Definition: RepoManager.cc:590
void init_knownRepositories()
Pathname metadataPath(const RepoInfo &info) const
Definition: RepoManager.cc:605
ServiceSet::size_type ServiceSizeType
Definition: RepoManager.h:118
std::set< RepoInfo > RepoSet
RepoInfo typedefs.
Definition: RepoManager.h:121
Pathname packagesPath(const RepoInfo &info) const
Definition: RepoManager.cc:608
DefaultIntegral< bool, false > _reposDirty
Definition: RepoManager.cc:726
RepoManagerOptions _options
Definition: RepoManager.cc:722
RepoStatus cacheStatus(const RepoInfo &info) const
Definition: RepoManager.cc:633
ServiceSet _services
Definition: RepoManager.cc:724
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:653
ServiceConstIterator serviceBegin() const
Definition: RepoManager.cc:652
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:591
void removeService(const ServiceInfo &service)
Definition: RepoManager.cc:670
ServiceSizeType serviceSize() const
Definition: RepoManager.cc:651
RepoSet::size_type RepoSizeType
Definition: RepoManager.h:123
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:658
Pathname generateNonExistingName(const Pathname &dir, const std::string &basefilename) const
void refreshServices(const RefreshServiceOptions &options_r)
RepoConstIterator repoBegin() const
Definition: RepoManager.cc:592
const RepoSet & repos() const
Iterate the known repositories.
Definition: RepoManager.cc:718
void init_knownServices()
Impl(const RepoManagerOptions &opt)
Definition: RepoManager.cc:537
RefreshServiceFlags RefreshServiceOptions
Options tuning RefreshService.
Definition: RepoManager.h:152
void getRepositoriesInService(const std::string &alias, OutputIterator out) const
Definition: RepoManager.cc:706
std::set< ServiceInfo > ServiceSet
ServiceInfo typedefs.
Definition: RepoManager.h:116
RepoConstIterator repoEnd() const
Definition: RepoManager.cc:593
void setCacheStatus(const RepoInfo &info, const RepoStatus &status)
Definition: RepoManager.cc:696
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:690
PluginRepoverification _pluginRepoverification
Definition: RepoManager.cc:728
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
Functor replacing repository variables.
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