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