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