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