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