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