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