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