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