libzypp  17.31.0
RepoManager.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
13 #include <cstdlib>
14 #include <iostream>
15 #include <fstream>
16 #include <sstream>
17 #include <list>
18 #include <map>
19 #include <algorithm>
20 
21 #include <solv/solvversion.h>
22 
23 #include <zypp-core/base/InputStream>
24 #include <zypp/base/LogTools.h>
25 #include <zypp/base/Gettext.h>
26 #include <zypp-core/base/DefaultIntegral>
27 #include <zypp/base/Function.h>
28 #include <zypp/base/Regex.h>
29 #include <zypp/PathInfo.h>
30 #include <zypp/TmpPath.h>
31 
32 #include <zypp/ServiceInfo.h>
34 #include <zypp/RepoManager.h>
35 
37 #include <zypp-media/auth/CredentialManager>
38 #include <zypp-media/MediaException>
39 #include <zypp/MediaSetAccess.h>
40 #include <zypp/ExternalProgram.h>
41 #include <zypp/ManagedFile.h>
42 
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 
603  void refreshMetadata( const RepoInfo & info, RawMetadataRefreshPolicy policy, OPT_PROGRESS );
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 
614  void cleanCacheDirGarbage( OPT_PROGRESS );
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:
701  void init_knownServices();
702  void init_knownRepositories();
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
RefreshCheckStatus checkIfToRefreshMetadata(const RepoInfo &info, const Url &url, RawMetadataRefreshPolicy policy)
std::string getScheme() const
Returns the scheme name of the URL.
Definition: Url.cc:533
std::string asString(const Patch::Category &obj)
Definition: Patch.cc:122
static const ValueType day
Definition: Date.h:44
RefreshCheckStatus
Possibly return state of checkIfRefreshMEtadata function.
Definition: RepoManager.h:196
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:319
Service data.
Definition: ServiceInfo.h:36
thrown when it was impossible to match a repository
Thrown when the repo alias is found to be invalid.
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition: Target.cc:102
RepoManagerOptions(const Pathname &root_r=Pathname())
Default ctor following ZConfig global settings.
Definition: RepoManager.cc:474
#define MIL
Definition: Logger.h:96
Pathname builtinRepoPackagesPath() const
The builtin config file value.
Definition: ZConfig.cc:998
static const std::string & sha1()
sha1
Definition: Digest.cc:35
void getRepositoriesInService(const std::string &alias, OutputIterator out) const
Definition: RepoManager.cc:692
int exchange(const Pathname &lpath, const Pathname &rpath)
Exchanges two files or directories.
Definition: PathInfo.cc:756
static bool error(const std::string &msg_r, const UserData &userData_r=UserData())
send error text
bool hasRepo(const std::string &alias) const
Definition: RepoManager.cc:583
thrown when it was impossible to determine this repo type.
std::string digest()
get hex string representation of the digest
Definition: Digest.cc:175
Retrieval of repository list for a service.
Definition: ServiceRepos.h:25
#define _(MSG)
Definition: Gettext.h:37
Repository metadata verification beyond GPG.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:428
Type toEnum() const
Definition: RepoType.h:48
static ManagedFile provideFileFromUrl(const Url &file_url, ProvideFileOptions options=PROVIDE_DEFAULT)
Provides file from url.
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:823
static TmpDir makeSibling(const Pathname &sibling_r)
Provide a new empty temporary directory as sibling.
Definition: TmpPath.cc:295
#define OPT_PROGRESS
Definition: RepoManager.cc:63
scoped_ptr< media::CredentialManager > _cmPtr
Definition: RepoManager.cc:146
Impl * clone() const
clone for RWCOW_pointer
Definition: RepoManager.cc:719
RepoStatus status(MediaSetAccess &media) override
Status of the remote repository.
Definition: Downloader.cc:35
void removeService(const std::string &alias)
ServiceInfo getService(const std::string &alias) const
Definition: RepoManager.cc:646
RepoSizeType repoSize() const
Definition: RepoManager.cc:579
void refreshServices(const RefreshServiceOptions &options_r)
Pathname builtinRepoMetadataPath() const
The builtin config file value.
Definition: ZConfig.cc:992
Pathname metadataPath(const RepoInfo &info) const
Definition: RepoManager.cc:593
bool repo_add_probe() const
Whether repository urls should be probed.
Definition: ZConfig.cc:1047
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
String related utilities and Regular expression matching.
RefreshServiceFlags RefreshServiceOptions
Options tuning RefreshService.
Definition: RepoManager.h:150
std::list< Url > url_set
Definition: RepoInfo.h:103
std::ostream & operator<<(std::ostream &str, const SerialNumber &obj)
Definition: SerialNumber.cc:52
What is known about a repository.
Definition: RepoInfo.h:71
ServiceSet::size_type ServiceSizeType
Definition: RepoManager.h:116
static bool warning(const std::string &msg_r, const UserData &userData_r=UserData())
send warning text
RepoInfo getRepositoryInfo(const std::string &alias, OPT_PROGRESS)
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition: ManagedFile.h:27
RepoSet & reposManip()
Definition: RepoManager.cc:705
void addRepositories(const Url &url, OPT_PROGRESS)
std::string targetDistro
Definition: RepoManager.cc:280
RepoInfo getRepo(const std::string &alias) const
Definition: RepoManager.cc:586
void reposErase(const std::string &alias_r)
Remove a Repository named alias_r.
Definition: Pool.h:112
Service already exists and some unique attribute can&#39;t be duplicated.
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
static RepoStatus fromCookieFile(const Pathname &path)
Reads the status from a cookie file.
Definition: RepoStatus.cc:194
Service without alias was used in an operation.
repo::ServiceType probeService(const Url &url) const
function< bool(const ProgressData &)> ReceiverFnc
Most simple version of progress reporting The percentage in most cases.
Definition: progressdata.h:139
void buildCache(const RepoInfo &info, CacheBuildPolicy policy, OPT_PROGRESS)
Url::asString() view options.
Definition: UrlBase.h:39
void modifyService(const std::string &oldAlias, const ServiceInfo &newService)
#define ERR
Definition: Logger.h:98
unsigned int MediaAccessId
Media manager access Id type.
Definition: MediaSource.h:29
std::vector< std::string > Arguments
PluginRepoverification _pluginRepoverification
Definition: RepoManager.cc:714
repo::RepoType probe(const Url &url, const Pathname &path=Pathname()) const
std::string generateFilename(const RepoInfo &info) const
Definition: RepoManager.cc:676
Repo manager settings.
Definition: RepoManager.h:53
void loadFromCache(const RepoInfo &info, OPT_PROGRESS)
boost::logic::tribool TriBool
3-state boolean logic (true, false and indeterminate).
Definition: String.h:30
std::string & replaceAll(std::string &str_r, const std::string &from_r, const std::string &to_r)
Replace all occurrences of from_r with to_r in str_r (inplace).
Definition: String.cc:330
transform_iterator< repo::RepoVariablesUrlReplacer, url_set::const_iterator > urls_const_iterator
Definition: RepoInfo.h:105
std::map< std::string, RepoState > RepoStates
Definition: ServiceInfo.h:185
static const ServiceType RIS
Repository Index Service (RIS) (formerly known as &#39;Novell Update&#39; (NU) service)
Definition: ServiceType.h:32
void saveToCookieFile(const Pathname &path_r) const
Save the status information to a cookie file.
Definition: RepoStatus.cc:212
std::set< ServiceInfo > ServiceSet
ServiceInfo typedefs.
Definition: RepoManager.h:111
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:440
void addRepository(const RepoInfo &info, OPT_PROGRESS)
Downloader for SUSETags (YaST2) repositories Encapsulates all the knowledge of which files have to be...
Definition: Downloader.h:34
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
Definition: NonCopyable.h:26
static Pool instance()
Singleton ctor.
Definition: Pool.h:55
static RepoManagerOptions makeTestSetup(const Pathname &root_r)
Test setup adjusting all paths to be located below one root_r directory.
Definition: RepoManager.cc:488
Pathname rootDir
remembers root_r value for later use
Definition: RepoManager.h:96
ServiceSet _services
Definition: RepoManager.cc:710
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:700
thrown when it was impossible to determine one url for this repo.
Definition: RepoException.h:78
RepoStatus status(MediaSetAccess &media_r) override
Status of the remote repository.
Definition: Downloader.cc:201
std::string alias() const
unique identifier for this source.
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:281
void addService(const std::string &alias, const Url &url)
Definition: RepoManager.cc:654
unsigned repo_refresh_delay() const
Amount of time in minutes that must pass before another refresh.
Definition: ZConfig.cc:1050
bool serviceEmpty() const
Definition: RepoManager.cc:638
std::set< RepoInfo > RepoSet
RepoInfo typedefs.
Definition: RepoManager.h:119
static const ServiceType NONE
No service set.
Definition: ServiceType.h:34
static const SolvAttr repositoryToolVersion
Definition: SolvAttr.h:174
Service type enumeration.
Definition: ServiceType.h:26
std::ostream & operator<<(std::ostream &str, const DeltaCandidates &obj)
static Pathname assertprefix(const Pathname &root_r, const Pathname &path_r)
Return path_r prefixed with root_r, unless it is already prefixed.
Definition: Pathname.cc:271
int recursive_rmdir(const Pathname &path)
Like &#39;rm -r DIR&#39;.
Definition: PathInfo.cc:412
void cleanMetadata(const RepoInfo &info, OPT_PROGRESS)
#define WAR
Definition: Logger.h:97
bool repoEmpty() const
Definition: RepoManager.cc:578
#define OUTS(X)
std::string asCompleteString() const
Returns a complete string representation of the Url object.
Definition: Url.cc:505
time_t Duration
Definition: Date.h:39
RepoInfoList repos
Definition: RepoManager.cc:279
const RepoSet & repos() const
Iterate the known repositories.
Definition: RepoManager.cc:704
void updateSolvFileIndex(const Pathname &solvfile_r)
Create solv file content digest for zypper bash completion.
Definition: Pool.cc:286
void removeService(const ServiceInfo &service)
Definition: RepoManager.cc:658
static const ServiceType PLUGIN
Plugin services are scripts installed on your system that provide the package manager with repositori...
Definition: ServiceType.h:43
bool isCached(const RepoInfo &info) const
Definition: RepoManager.cc:618
Base Exception for service handling.
const Pathname & _root
Definition: RepoManager.cc:145
ServiceSet::const_iterator ServiceConstIterator
Definition: RepoManager.h:115
ServiceConstIterator serviceBegin() const
Definition: RepoManager.cc:640
const std::string & asString() const
Return current Pathname as String.
Definition: PathInfo.h:248
std::string numstring(char n, int w=0)
Definition: String.h:289
static const RepoType NONE
Definition: RepoType.h:32
Impl(const RepoManagerOptions &opt)
Definition: RepoManager.cc:525
int touch(const Pathname &path)
Change file&#39;s modification and access times.
Definition: PathInfo.cc:1234
ServiceConstIterator serviceEnd() const
Definition: RepoManager.cc:641
std::ostream & copy(std::istream &from_r, std::ostream &to_r)
Copy istream to ostream.
Definition: IOStream.h:50
bool userMayRX() const
Definition: PathInfo.h:350
static const RepoType RPMMD
Definition: RepoType.h:29
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:436
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition: PathInfo.cc:605
RWCOW_pointer< Impl > _pimpl
Pointer to implementation.
Definition: RepoManager.h:698
Pathname builtinRepoSolvfilesPath() const
The builtin config file value.
Definition: ZConfig.cc:995
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
void refreshService(const ServiceInfo &service, const RefreshServiceOptions &options_r)
Definition: RepoManager.cc:664
static const RepoType YAST2
Definition: RepoType.h:30
void refreshMetadata(const RepoInfo &info, RawMetadataRefreshPolicy policy, OPT_PROGRESS)
ServiceSet & _services
Definition: RepoManager.cc:452
thrown when it was impossible to determine an alias for this repo.
Definition: RepoException.h:91
RepoSet::size_type RepoSizeType
Definition: RepoManager.h:121
std::string generateFilename(const ServiceInfo &info) const
Definition: RepoManager.cc:679
Exception for repository handling.
Definition: RepoException.h:37
RepoConstIterator repoBegin() const
Definition: RepoManager.cc:580
static std::string makeStupidAlias(const Url &url_r=Url())
Some stupid string but suitable as alias for your url if nothing better is available.
media::MediaAccessId _mid
Definition: RepoManager.cc:187
static Date now()
Return the current time.
Definition: Date.h:78
bool ZYPP_PLUGIN_APPDATA_FORCE_COLLECT()
To trigger appdata refresh unconditionally.
Definition: RepoManager.cc:73
#define PL_(MSG1, MSG2, N)
Definition: Gettext.h:40
std::string getHost(EEncoding eflag=zypp::url::E_DECODED) const
Returns the hostname or IP from the URL authority.
Definition: Url.cc:588
Functor thats filter RepoInfo by service which it belongs to.
Definition: RepoManager.h:641
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition: String.h:429
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition: AutoDispose.h:93
The repository cache is not built yet so you can&#39;t create the repostories from the cache...
Definition: RepoException.h:65
time_t ValueType
Definition: Date.h:38
Pathname repoPackagesCachePath
Definition: RepoManager.h:82
static const ServiceInfo noService
Represents an empty service.
Definition: ServiceInfo.h:61
void removeRepository(const RepoInfo &info, OPT_PROGRESS)
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
static const RepoInfo noRepo
Represents no Repository (one with an empty alias).
Definition: RepoInfo.h:80
bool regex_match(const std::string &s, smatch &matches, const regex &regex)
regex ZYPP_STR_REGEX regex ZYPP_STR_REGEX
Definition: Regex.h:70
Thrown when the repo alias is found to be invalid.
static const RepoType RPMPLAINDIR
Definition: RepoType.h:31
static const std::string & systemRepoAlias()
Reserved system repository alias .
Definition: Repository.cc:37
RepoStatus metadataStatus(const RepoInfo &info) const
Track changing files or directories.
Definition: RepoStatus.h:40
void modifyRepository(const std::string &alias, const RepoInfo &newinfo_r, OPT_PROGRESS)
Repository already exists and some unique attribute can&#39;t be duplicated.
ServiceSizeType serviceSize() const
Definition: RepoManager.cc:639
void cleanPackages(const RepoInfo &info, OPT_PROGRESS)
RepoManagerOptions _options
Definition: RepoManager.cc:708
void refreshService(const std::string &alias, const RefreshServiceOptions &options_r)
Repository addRepoSolv(const Pathname &file_r, const std::string &name_r)
Load Solvables from a solv-file into a Repository named name_r.
Definition: Pool.cc:185
Downloader for YUM (rpm-nmd) repositories Encapsulates all the knowledge of which files have to be do...
Definition: Downloader.h:40
void addService(const ServiceInfo &service)
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
RepoManager(const RepoManagerOptions &options=RepoManagerOptions())
RepoConstIterator repoEnd() const
Definition: RepoManager.cc:581
std::string hexstring(char n, int w=4)
Definition: String.h:324
RepoManager implementation.
void cleanCacheDirGarbage(OPT_PROGRESS)
std::ostream & operator<<(std::ostream &str, const RepoManager::Impl &obj)
Definition: RepoManager.cc:725
Service has no or invalid url defined.
Url manipulation class.
Definition: Url.h:91
RepoStatus cacheStatus(const RepoInfo &info) const
Definition: RepoManager.cc:621
bool hasService(const std::string &alias) const
Definition: RepoManager.cc:643
#define ZYPP_LOCAL
Definition: Globals.h:59
void cleanCache(const RepoInfo &info, OPT_PROGRESS)
#define Z_CHKGPG(I, N)
#define DBG
Definition: Logger.h:95
RepoSet::const_iterator RepoConstIterator
Definition: RepoManager.h:120
void setCacheStatus(const RepoInfo &info, const RepoStatus &status)
Definition: RepoManager.cc:682
Repository type enumeration.
Definition: RepoType.h:27
DefaultIntegral< bool, false > _reposDirty
Definition: RepoManager.cc:712
Pathname packagesPath(const RepoInfo &info) const
Definition: RepoManager.cc:596