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