libzypp  17.37.10
repomanager.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
9 
10 #include "repomanager.h"
12 
13 #include <solv/solvversion.h>
14 
15 #include <zypp-core/AutoDispose.h>
16 #include <zypp-core/base/Regex.h>
17 #include <zypp-core/fs/PathInfo.h>
18 #include <zypp-core/zyppng/pipelines/MTry>
19 #include <zypp-core/zyppng/pipelines/Transform>
20 #include <zypp-core/zyppng/ui/ProgressObserver>
22 #include <zypp/HistoryLog.h>
23 #include <zypp/ZConfig.h>
24 #include <zypp/ZYppCallbacks.h>
25 #include <zypp/base/LogTools.h>
28 #include <zypp/sat/Pool.h>
30 #include <zypp/repo/ServiceType.h>
32 
33 #include <zypp/ng/reporthelper.h>
34 #include <zypp/ng/repo/refresh.h>
38 
39 #include <fstream>
40 #include <utility>
41 
42 #undef ZYPP_BASE_LOGGER_LOGGROUP
43 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::repomanager"
44 
46  bool IGotIt(); // in readonly-mode
47 }
48 
49 
50 namespace zyppng
51 {
52  namespace env
53  {
56  {
57  const char * env = getenv("ZYPP_PLUGIN_APPDATA_FORCE_COLLECT");
58  return( env && zypp::str::strToBool( env, true ) );
59  }
60  } // namespace env
61 
62  namespace {
68  inline void cleanupNonRepoMetadataFolders( const zypp::Pathname & cachePath_r,
69  const zypp::Pathname & defaultCachePath_r,
70  const std::list<std::string> & repoEscAliases_r )
71  {
72  if ( cachePath_r != defaultCachePath_r )
73  return;
74 
75  std::list<std::string> entries;
76  if ( zypp::filesystem::readdir( entries, cachePath_r, false ) == 0 )
77  {
78  entries.sort();
79  std::set<std::string> oldfiles;
80  set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
81  std::inserter( oldfiles, oldfiles.end() ) );
82 
83  // bsc#1178966: Files or symlinks here have been created by the user
84  // for whatever purpose. It's our cache, so we purge them now before
85  // they may later conflict with directories we need.
86  zypp::PathInfo pi;
87  for ( const std::string & old : oldfiles )
88  {
89  if ( old == zypp::Repository::systemRepoAlias() ) // don't remove the @System solv file
90  continue;
91  pi( cachePath_r/old );
92  if ( pi.isDir() )
94  else
96  }
97  }
98  }
99  } // namespace
100 
102  {
103  switch ( obj ) {
104 #define OUTS(V) case zypp::RepoManagerFlags::V: str << #V; break
106  OUTS( RefreshForced );
108 #undef OUTS
109  }
110  return str;
111  }
112 
113  std::ostream & operator<<( std::ostream & str, zypp::RepoManagerFlags::RefreshCheckStatus obj )
114  {
115  switch ( obj ) {
116 #define OUTS(V) case zypp::RepoManagerFlags::V: str << #V; break
117  OUTS( REFRESH_NEEDED );
120 #undef OUTS
121  }
122  return str;
123  }
124 
125  std::ostream & operator<<( std::ostream & str, zypp::RepoManagerFlags::CacheBuildPolicy obj )
126  {
127  switch ( obj ) {
128 #define OUTS(V) case zypp::RepoManagerFlags::V: str << #V; break
129  OUTS( BuildIfNeeded );
130  OUTS( BuildForced );
131 #undef OUTS
132  }
133  return str;
134  }
135 
136 
137  std::string filenameFromAlias(const std::string &alias_r, const std::string &stem_r)
138  {
139  std::string filename( alias_r );
140  // replace slashes with underscores
141  zypp::str::replaceAll( filename, "/", "_" );
142 
143  filename = zypp::Pathname(filename).extend("."+stem_r).asString();
144  MIL << "generating filename for " << stem_r << " [" << alias_r << "] : '" << filename << "'" << std::endl;
145  return filename;
146  }
147 
149  {
150  // skip repositories meant for other distros than specified
151  if (!targetDistro.empty()
152  && !repo.targetDistribution().empty()
153  && repo.targetDistribution() != targetDistro)
154  {
155  MIL
156  << "Skipping repository meant for '" << repo.targetDistribution()
157  << "' distribution (current distro is '"
158  << targetDistro << "')." << std::endl;
159 
160  return true;
161  }
162 
163  repos.push_back(repo);
164  return true;
165  }
166 
168  {
169  try {
170  MIL << "repo file: " << file << std::endl;
171  RepoCollector collector;
172  zypp::parser::RepoFileReader parser( file, std::bind( &RepoCollector::collect, &collector, std::placeholders::_1 ) );
173  return expected<std::list<RepoInfo>>::success( std::move(collector.repos) );
174  } catch ( ... ) {
176  }
177  }
178 
188  template <typename ZContextRef>
189  std::list<RepoInfo> repositories_in_dir( ZContextRef zyppContext, const zypp::Pathname &dir )
190  {
191  MIL << "directory " << dir << std::endl;
192  std::list<RepoInfo> repos;
193  bool nonroot( geteuid() != 0 );
194  if ( nonroot && ! zypp::PathInfo(dir).userMayRX() )
195  {
196  JobReportHelper(zyppContext).warning( zypp::str::Format(_("Cannot read repo directory '%1%': Permission denied")) % dir );
197  }
198  else
199  {
200  std::list<zypp::Pathname> entries;
201  if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
202  {
203  // TranslatorExplanation '%s' is a pathname
204  ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
205  }
206 
207  zypp::str::regex allowedRepoExt("^\\.repo(_[0-9]+)?$");
208  for ( std::list<zypp::Pathname>::const_iterator it = entries.begin(); it != entries.end(); ++it )
209  {
210  if ( zypp::str::regex_match(it->extension(), allowedRepoExt) )
211  {
212  if ( nonroot && ! zypp::PathInfo(*it).userMayR() )
213  {
214  JobReportHelper(zyppContext).warning( zypp::str::Format(_("Cannot read repo file '%1%': Permission denied")) % *it );
215  }
216  else
217  {
218  const std::list<RepoInfo> tmp( repositories_in_file( *it ).unwrap() );
219  repos.insert( repos.end(), tmp.begin(), tmp.end() );
220  }
221  }
222  }
223  }
224  return repos;
225  }
226 
228  {
229  if ( info.repoOriginsEmpty() )
231  return expected<void>::success();
232  }
233 
234  bool autoPruneInDir(const zypp::Pathname &path_r)
235  { return not zypp::PathInfo(path_r/".no_auto_prune").isExist(); }
236 
237 
238  template <typename ZyppContextRefType>
240  : _zyppContext( std::move(zyppCtx) )
241  , _options( std::move(opt) )
242  , _pluginRepoverification( _options.pluginsPath / "repoverification",
243  _options.rootDir)
244  {
245 
246  }
247 
248  template <typename ZyppContextRefType>
250  {
251  // trigger appdata refresh if some repos change
252  if ( ( _reposDirty || env::ZYPP_PLUGIN_APPDATA_FORCE_COLLECT() )
253  && geteuid() == 0 && ( _options.rootDir.empty() || _options.rootDir == "/" ) )
254  {
255  try {
256  std::list<zypp::Pathname> entries;
257  zypp::filesystem::readdir( entries, _options.pluginsPath/"appdata", false );
258  if ( ! entries.empty() )
259  {
261  cmd.push_back( "<" ); // discard stdin
262  cmd.push_back( ">" ); // discard stdout
263  cmd.push_back( "PROGRAM" ); // [2] - fix index below if changing!
264  for ( const auto & rinfo : repos() )
265  {
266  if ( ! rinfo.enabled() )
267  continue;
268  cmd.push_back( "-R" );
269  cmd.push_back( rinfo.alias() );
270  cmd.push_back( "-t" );
271  cmd.push_back( rinfo.type().asString() );
272  cmd.push_back( "-p" );
273  cmd.push_back( (rinfo.metadataPath()/rinfo.path()).asString() ); // bsc#1197684: path to the repodata/ directory inside the cache
274  }
275 
276  for_( it, entries.begin(), entries.end() )
277  {
278  zypp::PathInfo pi( *it );
279  //DBG << "/tmp/xx ->" << pi << endl;
280  if ( pi.isFile() && pi.userMayRX() )
281  {
282  // trigger plugin
283  cmd[2] = pi.asString(); // [2] - PROGRAM
285  }
286  }
287  }
288  }
289  catch (...) {} // no throw in dtor
290  }
291  }
292 
293  template<typename ZyppContextRefType>
295  {
296  using namespace zyppng::operators;
297  return
298  init_knownServices()
299  | and_then( [this](){ return init_knownRepositories(); } );
300  }
301 
302  template<typename ZyppContextRefType>
304  {
305  return _options;
306  }
307 
308  template <typename ZyppContextRefType>
310  {
311  try {
312  using namespace zyppng::operators;
313 
314  // ATTENTION when making this pipeline async
315  // consider moving it into a workflow object
316  // this var is caputured by ref to modify it from
317  // inside the pipeline, which would break.
318  zypp::Pathname mediarootpath;
319 
320  return rawcache_path_for_repoinfo( options, info )
321  | and_then( [&]( zypp::Pathname mrPath ) {
322  mediarootpath = std::move(mrPath);
323  return rawproductdata_path_for_repoinfo( options, info );
324  })
325  | and_then( [&]( zypp::Pathname productdatapath ) {
326  zypp::repo::RepoType repokind = info.type();
327  // If unknown, probe the local metadata
328  if ( repokind == zypp::repo::RepoType::NONE )
329  repokind = probeCache( productdatapath );
330 
331  // NOTE: The calling code expects an empty RepoStatus being returned
332  // if the metadata cache is empty. So additional components like the
333  // RepoInfos status are joined after the switch IFF the status is not
334  // empty.huhu
335  RepoStatus status;
336  switch ( repokind.toEnum() )
337  {
339  status = RepoStatus( productdatapath/"repodata/repomd.xml");
340  if ( info.requireStatusWithMediaFile() )
341  status = status && RepoStatus( mediarootpath/"media.1/media" );
342  break;
343 
345  status = RepoStatus( productdatapath/"content" ) && RepoStatus( mediarootpath/"media.1/media" );
346  break;
347 
349  // Dir status at last refresh. Plaindir uses the cookiefile as pseudo metadata index file.
350  // It gets touched if the refresh check finds the data being up-to-date. That's why we use
351  // the files mtime as timestamp (like the RepoStatus ctor in the other cases above).
352  status = RepoStatus::fromCookieFileUseMtime( productdatapath/"cookie" );
353  break;
354 
356  // Return default RepoStatus in case of RepoType::NONE
357  // indicating it should be created?
358  // ZYPP_THROW(RepoUnknownTypeException());
359  break;
360  }
361 
362  if ( ! status.empty() )
363  status = status && RepoStatus( info );
364 
365  return expected<RepoStatus>::success(status);
366  });
367  } catch (...) {
369  }
370  }
371 
372  template <typename ZyppContextRefType>
374  {
375  return metadataStatus( info, _options );
376  }
377 
378  template <typename ZyppContextRefType>
379  expected<void> RepoManager<ZyppContextRefType>::cleanMetadata(const RepoInfo &info, ProgressObserverRef myProgress )
380  {
381  try {
382 
383  ProgressObserver::setup( myProgress, _("Cleaning metadata"), 100 );
384  ProgressObserver::start( myProgress );
385  zypp::filesystem::recursive_rmdir( _zyppContext->config().geoipCachePath() );
386  ProgressObserver::setCurrent ( myProgress, 50 );
388  ProgressObserver::finish ( myProgress );
389 
390  } catch ( ... ) {
391  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
393  }
394  return expected<void>::success();
395  }
396 
397  template <typename ZyppContextRefType>
398  expected<void> RepoManager<ZyppContextRefType>::cleanPackages(const RepoInfo &info, ProgressObserverRef myProgress, bool isAutoClean )
399  {
400  try {
401  ProgressObserver::setup( myProgress, _("Cleaning packages"), 100 );
402  ProgressObserver::start( myProgress );
403 
404  // bsc#1204956: Tweak to prevent auto pruning package caches
406  if ( not isAutoClean || autoPruneInDir( rpc.dirname() ) )
408 
409  ProgressObserver::finish ( myProgress );
410 
411  } catch (...) {
412  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
414  }
415 
416  return expected<void>::success();
417  }
418 
424  template <typename ZyppContextRefType>
426  {
427  MIL << "going to probe the cached repo at " << path_r << std::endl;
428 
430 
431  if ( zypp::PathInfo(path_r/"/repodata/repomd.xml").isFile() )
432  { ret = zypp::repo::RepoType::RPMMD; }
433  else if ( zypp::PathInfo(path_r/"/content").isFile() )
434  { ret = zypp::repo::RepoType::YAST2; }
435  else if ( zypp::PathInfo(path_r/"/cookie").isFile() )
437 
438  MIL << "Probed cached type " << ret << " at " << path_r << std::endl;
439  return ret;
440  }
441 
442  template <typename ZyppContextRefType>
444  {
445  try {
446  MIL << "Going to clean up garbage in cache dirs" << std::endl;
447 
448  std::list<zypp::Pathname> cachedirs;
449  cachedirs.push_back(_options.repoRawCachePath);
450  cachedirs.push_back(_options.repoPackagesCachePath);
451  cachedirs.push_back(_options.repoSolvCachePath);
452 
453  ProgressObserver::setup( myProgress, _("Cleaning up cache dirs"), cachedirs.size() );
454  ProgressObserver::start( myProgress );
455 
456  for( const auto &dir : cachedirs )
457  {
458  // increase progress on end of every iteration
459  zypp_defer {
460  ProgressObserver::increase( myProgress );
461  };
462 
463  if ( zypp::PathInfo(dir).isExist() )
464  {
465  std::list<zypp::Pathname> entries;
466  if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
467  // TranslatorExplanation '%s' is a pathname
468  ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
469 
470  if ( !entries.size() )
471  continue;
472 
473  auto dirProgress = ProgressObserver::makeSubTask( myProgress, 1.0, zypp::str::Format( _("Cleaning up directory: %1%") ) % dir, entries.size() );
474  for( const auto &subdir : entries )
475  {
476  // if it does not belong known repo, make it disappear
477  bool found = false;
478  for_( r, repoBegin(), repoEnd() )
479  if ( subdir.basename() == r->escaped_alias() )
480  { found = true; break; }
481 
482  if ( ! found && ( zypp::Date::now()-zypp::PathInfo(subdir).mtime() > zypp::Date::day ) )
484 
485  ProgressObserver::increase( dirProgress );
486  }
487  ProgressObserver::finish( dirProgress );
488  }
489  }
490  } catch (...) {
491  // will finish all subprogress children
492  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
494  }
495  ProgressObserver::finish ( myProgress );
496  return expected<void>::success();
497  }
498 
499  template <typename ZyppContextRefType>
500  expected<void> RepoManager<ZyppContextRefType>::cleanCache(const RepoInfo &info, ProgressObserverRef myProgress )
501  {
502  try {
503  ProgressObserver::setup( myProgress, _("Cleaning cache"), 100 );
504  ProgressObserver::start( myProgress );
505 
506  MIL << "Removing raw metadata cache for " << info.alias() << std::endl;
508 
509  ProgressObserver::finish( myProgress );
510  return expected<void>::success();
511 
512  } catch (...) {
513  // will finish all subprogress children
514  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
516  }
517  }
518 
519  template <typename ZyppContextRefType>
520  expected<void> RepoManager<ZyppContextRefType>::loadFromCache( const RepoInfo & info, ProgressObserverRef myProgress )
521  {
522  using namespace zyppng::operators;
523  return zyppng::mtry( [this, info, myProgress](){
524  ProgressObserver::setup( myProgress, _("Loading from cache"), 3 );
525  ProgressObserver::start( myProgress );
526 
527  assert_alias(info).unwrap();
528  zypp::Pathname solvfile = solv_path_for_repoinfo(_options, info).unwrap() / "solv";
529 
530  if ( ! zypp::PathInfo(solvfile).isExist() )
532 
533  _zyppContext->satPool().reposErase( info.alias() );
534 
535  ProgressObserver::increase ( myProgress );
536 
537  zypp::Repository repo = _zyppContext->satPool().addRepoSolv( solvfile, info );
538 
539  ProgressObserver::increase ( myProgress );
540 
541  // test toolversion in order to rebuild solv file in case
542  // it was written by a different libsolv-tool parser.
543  const std::string & toolversion( zypp::sat::LookupRepoAttr( zypp::sat::SolvAttr::repositoryToolVersion, repo ).begin().asString() );
544  if ( toolversion != LIBSOLV_TOOLVERSION ) {
545  repo.eraseFromPool();
546  ZYPP_THROW(zypp::Exception(zypp::str::Str() << "Solv-file was created by '"<<toolversion<<"'-parser (want "<<LIBSOLV_TOOLVERSION<<")."));
547  }
548  })
549  | or_else( [this, info, myProgress]( std::exception_ptr exp ) {
550  ZYPP_CAUGHT( exp );
551  MIL << "Try to handle exception by rebuilding the solv-file" << std::endl;
552  return cleanCache( info, ProgressObserver::makeSubTask( myProgress ) )
553  | and_then([this, info, myProgress]{
554  return buildCache ( info, zypp::RepoManagerFlags::BuildIfNeeded, ProgressObserver::makeSubTask( myProgress ) );
555  })
556  | and_then( mtry([this, info = info]{
557  _zyppContext->satPool().addRepoSolv( solv_path_for_repoinfo(_options, info).unwrap() / "solv", info );
558  }));
559  })
560  | and_then([myProgress]{
561  ProgressObserver::finish ( myProgress );
562  return expected<void>::success();
563  })
564  | or_else([myProgress]( auto ex ){
565  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
566  return expected<void>::error(ex);
567  })
568  ;
569  }
570 
571  template <typename ZyppContextRefType>
573  {
574  try {
575  auto tosave = info;
576 
577  // assert the directory exists
578  zypp::filesystem::assert_dir(_options.knownReposPath);
579 
580  zypp::Pathname repofile = generateNonExistingName(
581  _options.knownReposPath, generateFilename(tosave));
582  // now we have a filename that does not exists
583  MIL << "Saving repo in " << repofile << std::endl;
584 
585  std::ofstream file(repofile.c_str());
586  if (!file)
587  {
588  // TranslatorExplanation '%s' is a filename
589  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), repofile.c_str() )));
590  }
591 
592  tosave.dumpAsIniOn(file);
593  tosave.setFilepath(repofile);
594  tosave.setMetadataPath( rawcache_path_for_repoinfo( _options, tosave ).unwrap() );
595  tosave.setPackagesPath( packagescache_path_for_repoinfo( _options, tosave ).unwrap() );
596  reposManip().insert(tosave);
597 
598  // check for credentials in base Urls
599  zypp::UrlCredentialExtractor( _options.rootDir ).collect( tosave.baseUrls() );
600 
601  zypp::HistoryLog(_options.rootDir).addRepository(tosave);
602 
603  // return the new repoinfo
604  return expected<RepoInfo>::success( tosave );
605 
606  } catch (...) {
608  }
609  }
610 
611  template <typename ZyppContextRefType>
612  expected<void> RepoManager<ZyppContextRefType>::removeRepository( const RepoInfo & info, ProgressObserverRef myProgress )
613  {
614  try {
615  ProgressObserver::setup( myProgress, zypp::str::form(_("Removing repository '%s'"), info.label().c_str()), 1 );
616  ProgressObserver::start( myProgress );
617 
618  MIL << "Going to delete repo " << info.alias() << std::endl;
619 
620  for( const auto &repo : repos() )
621  {
622  // they can be the same only if the provided is empty, that means
623  // the provided repo has no alias
624  // then skip
625  if ( (!info.alias().empty()) && ( info.alias() != repo.alias() ) )
626  continue;
627 
628  // TODO match by url
629 
630  // we have a matching repository, now we need to know
631  // where it does come from.
632  RepoInfo todelete = repo;
633  if (todelete.filepath().empty())
634  {
635  ZYPP_THROW(zypp::repo::RepoException( todelete, _("Can't figure out where the repo is stored.") ));
636  }
637  else
638  {
639  // figure how many repos are there in the file:
640  std::list<RepoInfo> filerepos = repositories_in_file(todelete.filepath()).unwrap();
641  if ( filerepos.size() == 0 // bsc#984494: file may have already been deleted
642  ||(filerepos.size() == 1 && filerepos.front().alias() == todelete.alias() ) )
643  {
644  // easy: file does not exist, contains no or only the repo to delete: delete the file
645  int ret = zypp::filesystem::unlink( todelete.filepath() );
646  if ( ! ( ret == 0 || ret == ENOENT ) )
647  {
648  // TranslatorExplanation '%s' is a filename
649  ZYPP_THROW(zypp::repo::RepoException( todelete, zypp::str::form( _("Can't delete '%s'"), todelete.filepath().c_str() )));
650  }
651  MIL << todelete.alias() << " successfully deleted." << std::endl;
652  }
653  else
654  {
655  // there are more repos in the same file
656  // write them back except the deleted one.
657  //TmpFile tmp;
658  //std::ofstream file(tmp.path().c_str());
659 
660  // assert the directory exists
662 
663  std::ofstream file(todelete.filepath().c_str());
664  if (!file)
665  {
666  // TranslatorExplanation '%s' is a filename
667  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), todelete.filepath().c_str() )));
668  }
669  for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
670  fit != filerepos.end();
671  ++fit )
672  {
673  if ( (*fit).alias() != todelete.alias() )
674  (*fit).dumpAsIniOn(file);
675  }
676  }
677 
678  // now delete it from cache
679  if ( isCached(todelete) )
680  cleanCache( todelete, ProgressObserver::makeSubTask( myProgress, 0.2 )).unwrap();
681  // now delete metadata (#301037)
682  cleanMetadata( todelete, ProgressObserver::makeSubTask( myProgress, 0.4 )).unwrap();
683  cleanPackages( todelete, ProgressObserver::makeSubTask( myProgress, 0.4 ), true/*isAutoClean*/ ).unwrap();
684  reposManip().erase(todelete);
685  MIL << todelete.alias() << " successfully deleted." << std::endl;
686  zypp::HistoryLog(_options.rootDir).removeRepository(todelete);
687 
688  ProgressObserver::finish(myProgress);
689  return expected<void>::success();
690  } // else filepath is empty
691  }
692  // should not be reached on a sucess workflow
694  } catch (...) {
695  ProgressObserver::finish( myProgress, ProgressObserver::Error );
696  return expected<void>::error( std::current_exception () );
697  }
698  }
699 
700  template <typename ZyppContextRefType>
701  expected<RepoInfo> RepoManager<ZyppContextRefType>::modifyRepository( const std::string & alias, const RepoInfo & newinfo_r, ProgressObserverRef myProgress )
702  {
703  try {
704 
705  ProgressObserver::setup( myProgress, _("Modifying repository"), 5 );
706  ProgressObserver::start( myProgress );
707 
708  RepoInfo toedit = getRepositoryInfo(alias).unwrap();
709  RepoInfo newinfo( newinfo_r ); // need writable copy to upadte housekeeping data
710 
711  // check if the new alias already exists when renaming the repo
712  if ( alias != newinfo.alias() && hasRepo( newinfo.alias() ) )
713  {
715  }
716 
717  if (toedit.filepath().empty())
718  {
719  ZYPP_THROW(zypp::repo::RepoException( toedit, _("Can't figure out where the repo is stored.") ));
720  }
721  else
722  {
723  ProgressObserver::increase( myProgress );
724  // figure how many repos are there in the file:
725  std::list<RepoInfo> filerepos = repositories_in_file(toedit.filepath()).unwrap();
726 
727  // there are more repos in the same file
728  // write them back except the deleted one.
729  //TmpFile tmp;
730  //std::ofstream file(tmp.path().c_str());
731 
732  // assert the directory exists
734 
735  std::ofstream file(toedit.filepath().c_str());
736  if (!file)
737  {
738  // TranslatorExplanation '%s' is a filename
739  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), toedit.filepath().c_str() )));
740  }
741  for ( std::list<RepoInfo>::const_iterator fit = filerepos.begin();
742  fit != filerepos.end();
743  ++fit )
744  {
745  // if the alias is different, dump the original
746  // if it is the same, dump the provided one
747  if ( (*fit).alias() != toedit.alias() )
748  (*fit).dumpAsIniOn(file);
749  else
750  newinfo.dumpAsIniOn(file);
751  }
752 
753  ProgressObserver::increase( myProgress );
754 
755  if ( toedit.enabled() && !newinfo.enabled() )
756  {
757  // On the fly remove solv.idx files for bash completion if a repo gets disabled.
758  const zypp::Pathname solvidx = solv_path_for_repoinfo(_options, newinfo).unwrap()/"solv.idx";
759  if ( zypp::PathInfo(solvidx).isExist() )
760  zypp::filesystem::unlink( solvidx );
761  }
762 
763  newinfo.setFilepath(toedit.filepath());
764  newinfo.setMetadataPath( rawcache_path_for_repoinfo( _options, newinfo ).unwrap() );
765  newinfo.setPackagesPath( packagescache_path_for_repoinfo( _options, newinfo ).unwrap() );
766 
767  ProgressObserver::increase( myProgress );
768 
769  reposManip().erase(toedit);
770  reposManip().insert(newinfo);
771 
772  ProgressObserver::increase( myProgress );
773 
774  // check for credentials in Urls
775  zypp::UrlCredentialExtractor( _options.rootDir ).collect( newinfo.baseUrls() );
776  zypp::HistoryLog(_options.rootDir).modifyRepository(toedit, newinfo);
777  MIL << "repo " << alias << " modified" << std::endl;
778 
779  ProgressObserver::finish ( myProgress );
780  return expected<RepoInfo>::success( newinfo );
781  }
782 
783  } catch ( ... ) {
784  ProgressObserver::finish ( myProgress, ProgressObserver::Error );
786  }
787  }
788 
789  template <typename ZyppContextRefType>
791  {
792  try {
793  RepoConstIterator it( findAlias( alias, repos() ) );
794  if ( it != repos().end() )
795  return make_expected_success(*it);
796  RepoInfo info;
797  info.setAlias( alias );
799  } catch ( ... ) {
800  return expected<RepoInfo>::error( std::current_exception () );
801  }
802  }
803 
804 
805  template <typename ZyppContextRefType>
807  {
808  try {
809 
810  for_( it, repoBegin(), repoEnd() )
811  {
812  for( const auto &origin : it->repoOrigins() )
813  {
814  if ( std::any_of( origin.begin(), origin.end(), [&url, &urlview]( const zypp::OriginEndpoint &ep ){ return (ep.url().asString(urlview) == url.asString(urlview)); }) )
815  return make_expected_success(*it);
816  }
817  }
818  RepoInfo info;
819  info.setBaseUrl( url );
821 
822  } catch ( ... ) {
823  return expected<RepoInfo>::error( std::current_exception () );
824  }
825  }
826 
827  template<typename ZyppContextRefType>
829  {
830  using namespace zyppng::operators;
831  return joinPipeline( _zyppContext,
833  | [this, info](auto) { return zyppng::repo::RefreshContext<ZyppContextRefType>::create( _zyppContext, info, shared_this<RepoManager<ZyppContextRefType>>() ); }
834  | and_then( [this, origin, policy]( zyppng::repo::RefreshContextRef<ZyppContextRefType> &&refCtx ) {
835  refCtx->setPolicy ( static_cast<zyppng::repo::RawMetadataRefreshPolicy>( policy ) );
836 
837  return _zyppContext->provider()->prepareMedia( origin, zyppng::ProvideMediaSpec() )
838  | and_then( [ r = std::move(refCtx) ]( auto mediaHandle ) mutable { return zyppng::RepoManagerWorkflow::checkIfToRefreshMetadata ( std::move(r), std::move(mediaHandle), nullptr ); } );
839  })
840  );
841  }
842 
843  template<typename ZyppContextRefType>
845  {
846  using namespace zyppng::operators;
847  // helper callback in case the repo type changes on the remote
848  // do NOT capture by reference here, since this is possibly executed async
849  const auto &updateProbedType = [this, info = info]( zypp::repo::RepoType repokind ) {
850  // update probed type only for repos in system
851  for( const auto &repo : repos() ) {
852  if ( info.alias() == repo.alias() )
853  {
854  RepoInfo modifiedrepo = repo;
855  modifiedrepo.setType( repokind );
856  // don't modify .repo in refresh.
857  // modifyRepository( info.alias(), modifiedrepo );
858  break;
859  }
860  }
861  };
862 
863  // the list of URLs we want to have geo ip redirects for
864  auto urls = info.baseUrls ();
865  if ( info.mirrorListUrl ().isValid () )
866  urls.push_back ( info.mirrorListUrl () );
867 
868  return joinPipeline( _zyppContext,
869  // make sure geoIP data is up 2 date, but ignore errors
871  | [this, info = info](auto) { return zyppng::repo::RefreshContext<ZyppContextRefType>::create( _zyppContext, info, shared_this<RepoManager<ZyppContextRefType>>()); }
872  | and_then( [policy, myProgress, cb = updateProbedType]( repo::RefreshContextRef<ZyppContextRefType> refCtx ) {
873  refCtx->setPolicy( static_cast<repo::RawMetadataRefreshPolicy>( policy ) );
874  // in case probe detects a different repokind, update our internal repos
876 
877  return zyppng::RepoManagerWorkflow::refreshMetadata ( std::move(refCtx), myProgress );
878  })
879  | and_then([rMgr = shared_this<RepoManager<ZyppContextRefType>>()]( repo::RefreshContextRef<ZyppContextRefType> ctx ) {
880 
881  if ( ! isTmpRepo( ctx->repoInfo() ) )
882  rMgr->reposManip(); // remember to trigger appdata refresh
883 
884  return expected<void>::success ();
885  }));
886  }
887 
888  template<typename ZyppContextRefType>
889  std::vector<std::pair<RepoInfo, expected<void>>> RepoManager<ZyppContextRefType>::refreshMetadata( std::vector<RepoInfo> infos, RawMetadataRefreshPolicy policy, ProgressObserverRef myProgress )
890  {
891  using namespace zyppng::operators;
892 
893  ProgressObserver::setup( myProgress, "Refreshing repositories" , 1 );
894 
895  auto r = std::move(infos)
896  | transform( [this, policy, myProgress]( const RepoInfo &info ) {
897 
898  auto subProgress = ProgressObserver::makeSubTask( myProgress, 1.0, zypp::str::Str() << _("Refreshing Repository: ") << info.alias(), 3 );
899 
900  // helper callback in case the repo type changes on the remote
901  // do NOT capture by reference here, since this is possibly executed async
902  const auto &updateProbedType = [this, info = info]( zypp::repo::RepoType repokind ) {
903  // update probed type only for repos in system
904  for( const auto &repo : repos() ) {
905  if ( info.alias() == repo.alias() )
906  {
907  RepoInfo modifiedrepo = repo;
908  modifiedrepo.setType( repokind );
909  // don't modify .repo in refresh.
910  // modifyRepository( info.alias(), modifiedrepo );
911  break;
912  }
913  }
914  };
915 
916  auto sharedThis = shared_this<RepoManager<ZyppContextRefType>>();
917 
918  return
919  // make sure geoIP data is up 2 date, but ignore errors
921  | [sharedThis, info = info](auto) { return zyppng::repo::RefreshContext<ZyppContextRefType>::create( sharedThis->_zyppContext, info, sharedThis); }
922  | inspect( incProgress( subProgress ) )
923  | and_then( [policy, subProgress, cb = updateProbedType]( repo::RefreshContextRef<ZyppContextRefType> refCtx ) {
924  refCtx->setPolicy( static_cast<repo::RawMetadataRefreshPolicy>( policy ) );
925  // in case probe detects a different repokind, update our internal repos
927 
928  return zyppng::RepoManagerWorkflow::refreshMetadata ( std::move(refCtx), ProgressObserver::makeSubTask( subProgress ) );
929  })
930  | inspect( incProgress( subProgress ) )
931  | and_then([subProgress]( repo::RefreshContextRef<ZyppContextRefType> ctx ) {
932 
933  if ( ! isTmpRepo( ctx->repoInfo() ) )
934  ctx->repoManager()->reposManip(); // remember to trigger appdata refresh
935 
936  return zyppng::RepoManagerWorkflow::buildCache ( std::move(ctx), CacheBuildPolicy::BuildIfNeeded, ProgressObserver::makeSubTask( subProgress ) );
937  })
938  | inspect( incProgress( subProgress ) )
939  | [ info = info, subProgress ]( expected<repo::RefreshContextRef<ZyppContextRefType>> result ) {
940  if ( result ) {
941  ProgressObserver::finish( subProgress, ProgressObserver::Success );
942  return std::make_pair(info, expected<void>::success() );
943  } else {
944  ProgressObserver::finish( subProgress, ProgressObserver::Error );
945  return std::make_pair(info, expected<void>::error( result.error() ) );
946  }
947  };
948  }
949  | [myProgress]( auto res ) {
950  ProgressObserver::finish( myProgress, ProgressObserver::Success );
951  return res;
952  }
953  );
954 
955  return joinPipeline( _zyppContext, r );
956  }
957 
964  template<typename ZyppContextRefType>
966  {
967  using namespace zyppng::operators;
968 
969  RepoInfo::url_set allUrls;
970  std::transform( origin.begin (), origin.end(), std::back_inserter(allUrls), []( const zypp::OriginEndpoint &ep ){ return ep.url(); } );
971 
972  return joinPipeline( _zyppContext,
974  | [this, origin=origin](auto) { return _zyppContext->provider()->prepareMedia( origin, zyppng::ProvideMediaSpec() ); }
975  | and_then( [this, path = path]( auto mediaHandle ) {
976  return RepoManagerWorkflow::probeRepoType( _zyppContext, std::move(mediaHandle), path );
977  }));
978  }
979 
980  template<typename ZyppContextRefType>
981  expected<void> RepoManager<ZyppContextRefType>::buildCache( const RepoInfo &info, CacheBuildPolicy policy, ProgressObserverRef myProgress )
982  {
983  using namespace zyppng::operators;
984  return joinPipeline( _zyppContext,
986  | and_then( [policy, myProgress]( repo::RefreshContextRef<ZyppContextRefType> refCtx ) {
987  return zyppng::RepoManagerWorkflow::buildCache ( std::move(refCtx), policy, myProgress );
988  })
989  | and_then([]( auto ){ return expected<void>::success(); })
990  );
991  }
992 
993  template<typename ZyppContextRefType>
995  {
996  return joinPipeline( _zyppContext, RepoManagerWorkflow::addRepository( shared_this<RepoManager<ZyppContextRefType>>(), info, std::move(myProgress) ) );
997  }
998 
999  template<typename ZyppContextRefType>
1001  {
1002  using namespace zyppng::operators;
1003  return joinPipeline( _zyppContext, RepoManagerWorkflow::addRepositories( shared_this<RepoManager<ZyppContextRefType>>(), url, std::move(myProgress)));
1004  }
1005 
1006  template <typename ZyppContextRefType>
1008  {
1010  }
1011 
1012  template <typename ZyppContextRefType>
1014  {
1015  try {
1016 
1017  assert_alias( service ).unwrap();
1018 
1019  // check if service already exists
1020  if ( hasService( service.alias() ) )
1022 
1023  // Writable ServiceInfo is needed to save the location
1024  // of the .service file. Finaly insert into the service list.
1025  ServiceInfo toSave( service );
1026  saveService( toSave ).unwrap();
1027  _services.insert( toSave );
1028 
1029  // check for credentials in Url
1030  zypp::UrlCredentialExtractor( _options.rootDir ).collect( toSave.url() );
1031 
1032  MIL << "added service " << toSave.alias() << std::endl;
1033 
1034  } catch ( ... ) {
1035  return expected<void>::error( std::current_exception () );
1036  }
1037 
1038  return expected<void>::success();
1039  }
1040 
1041  template<typename ZyppContextRefType>
1043  {
1044  return joinPipeline ( _zyppContext, RepoServicesWorkflow::refreshService( shared_this<RepoManager<ZyppContextRefType>>(), getService( alias ), options_r ) );
1045  }
1046 
1050  template<typename ZyppContextRefType>
1052  {
1053  using namespace zyppng::operators;
1054  // copy the set of services since refreshService
1055  // can eventually invalidate the iterator
1056  ServiceSet servicesCopy( serviceBegin(), serviceEnd() );
1057 
1058  // convert the set into a vector, transform needs a container with push_back support
1059  std::vector<ServiceInfo> servicesVec;
1060  std::copy( std::make_move_iterator(servicesCopy.begin()), std::make_move_iterator(servicesCopy.end()), std::back_inserter(servicesVec));
1061 
1062  return joinPipeline( _zyppContext,
1063  std::move(servicesVec)
1064  | transform( [options_r, this]( ServiceInfo i ){ return RepoServicesWorkflow::refreshService( shared_this<RepoManager<ZyppContextRefType>>(), i, options_r ); } )
1065  | join()
1066  | collect()
1067  );
1068  }
1069 
1071 
1072  template <typename ZyppContextRefType>
1074  {
1075  try {
1076  MIL << "Going to delete service " << alias << std::endl;
1077 
1078  const ServiceInfo & service = getService( alias );
1079 
1080  zypp::Pathname location = service.filepath();
1081  if( location.empty() )
1082  {
1083  ZYPP_THROW(zypp::repo::ServiceException( service, _("Can't figure out where the service is stored.") ));
1084  }
1085 
1086  ServiceSet tmpSet;
1088 
1089  // only one service definition in the file
1090  if ( tmpSet.size() == 1 )
1091  {
1092  if ( zypp::filesystem::unlink(location) != 0 )
1093  {
1094  // TranslatorExplanation '%s' is a filename
1095  ZYPP_THROW(zypp::repo::ServiceException( service, zypp::str::form( _("Can't delete '%s'"), location.c_str() ) ));
1096  }
1097  MIL << alias << " successfully deleted." << std::endl;
1098  }
1099  else
1100  {
1102 
1103  std::ofstream file(location.c_str());
1104  if( !file )
1105  {
1106  // TranslatorExplanation '%s' is a filename
1107  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), location.c_str() )));
1108  }
1109 
1110  for_(it, tmpSet.begin(), tmpSet.end())
1111  {
1112  if( it->alias() != alias )
1113  it->dumpAsIniOn(file);
1114  }
1115 
1116  MIL << alias << " successfully deleted from file " << location << std::endl;
1117  }
1118 
1119  // now remove all repositories added by this service
1120  RepoCollector rcollector;
1121  getRepositoriesInService( alias,
1122  boost::make_function_output_iterator( std::bind( &RepoCollector::collect, &rcollector, std::placeholders::_1 ) ) );
1123  // cannot do this directly in getRepositoriesInService - would invalidate iterators
1124  for_(rit, rcollector.repos.begin(), rcollector.repos.end())
1125  removeRepository(*rit).unwrap();
1126 
1127  return expected<void>::success();
1128 
1129  } catch ( ... ) {
1130  return expected<void>::error( std::current_exception () );
1131  }
1132  }
1133 
1134  template <typename ZyppContextRefType>
1135  expected<void> RepoManager<ZyppContextRefType>::modifyService( const std::string & oldAlias, const ServiceInfo & newService )
1136  {
1137  try {
1138 
1139  MIL << "Going to modify service " << oldAlias << std::endl;
1140 
1141  // we need a writable copy to link it to the file where
1142  // it is saved if we modify it
1143  ServiceInfo service(newService);
1144 
1145  if ( service.type() == zypp::repo::ServiceType::PLUGIN )
1146  {
1148  }
1149 
1150  const ServiceInfo & oldService = getService(oldAlias);
1151 
1152  zypp::Pathname location = oldService.filepath();
1153  if( location.empty() )
1154  {
1155  ZYPP_THROW(zypp::repo::ServiceException( oldService, _("Can't figure out where the service is stored.") ));
1156  }
1157 
1158  // remember: there may multiple services being defined in one file:
1159  ServiceSet tmpSet;
1161 
1163  std::ofstream file(location.c_str());
1164  for_(it, tmpSet.begin(), tmpSet.end())
1165  {
1166  if( *it != oldAlias )
1167  it->dumpAsIniOn(file);
1168  }
1169  service.dumpAsIniOn(file);
1170  file.close();
1171  service.setFilepath(location);
1172 
1173  _services.erase(oldAlias);
1174  _services.insert(service);
1175  // check for credentials in Urls
1176  zypp::UrlCredentialExtractor( _options.rootDir ).collect( service.url() );
1177 
1178 
1179  // changed properties affecting also repositories
1180  if ( oldAlias != service.alias() // changed alias
1181  || oldService.enabled() != service.enabled() ) // changed enabled status
1182  {
1183  std::vector<RepoInfo> toModify;
1184  getRepositoriesInService(oldAlias, std::back_inserter(toModify));
1185  for_( it, toModify.begin(), toModify.end() )
1186  {
1187  if ( oldService.enabled() != service.enabled() )
1188  {
1189  if ( service.enabled() )
1190  {
1191  // reset to last refreshs state
1192  const auto & last = service.repoStates().find( it->alias() );
1193  if ( last != service.repoStates().end() )
1194  it->setEnabled( last->second.enabled );
1195  }
1196  else
1197  it->setEnabled( false );
1198  }
1199 
1200  if ( oldAlias != service.alias() )
1201  it->setService(service.alias());
1202 
1203  modifyRepository(it->alias(), *it).unwrap();
1204  }
1205  }
1206 
1207  return expected<void>::success();
1208 
1209  } catch ( ... ) {
1210  return expected<void>::error( std::current_exception () );
1211  }
1212 
1214  }
1215 
1216 
1217  template <typename ZyppContextRefType>
1219  {
1220  try {
1221 
1222  zypp::filesystem::assert_dir( _options.knownServicesPath );
1223  zypp::Pathname servfile = generateNonExistingName( _options.knownServicesPath,
1224  generateFilename( service ) );
1225  service.setFilepath( servfile );
1226 
1227  MIL << "saving service in " << servfile << std::endl;
1228 
1229  std::ofstream file( servfile.c_str() );
1230  if ( !file )
1231  {
1232  // TranslatorExplanation '%s' is a filename
1233  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
1234  }
1235  service.dumpAsIniOn( file );
1236  MIL << "done" << std::endl;
1237 
1238  return expected<void>::success();
1239 
1240  } catch ( ... ) {
1241  return expected<void>::error( std::current_exception () );
1242  }
1243  }
1244 
1260  template <typename ZyppContextRefType>
1262  const std::string & basefilename ) const
1263  {
1264  std::string final_filename = basefilename;
1265  int counter = 1;
1266  while ( zypp::PathInfo(dir + final_filename).isExist() )
1267  {
1268  final_filename = basefilename + "_" + zypp::str::numstring(counter);
1269  ++counter;
1270  }
1271  return dir + zypp::Pathname(final_filename);
1272  }
1273 
1274  template <typename ZyppContextRefType>
1276  {
1277  try {
1278  zypp::Pathname productdatapath = rawproductdata_path_for_repoinfo( options, info ).unwrap();
1279 
1280  zypp::repo::RepoType repokind = info.type();
1281  if ( repokind.toEnum() == zypp::repo::RepoType::NONE_e )
1282  // unknown, probe the local metadata
1283  repokind = probeCache( productdatapath );
1284  // if still unknown, just return
1285  if (repokind == zypp::repo::RepoType::NONE_e)
1286  return expected<void>::success();
1287 
1288  zypp::Pathname p;
1289  switch ( repokind.toEnum() )
1290  {
1292  p = zypp::Pathname(productdatapath + "/repodata/repomd.xml");
1293  break;
1294 
1296  p = zypp::Pathname(productdatapath + "/content");
1297  break;
1298 
1300  p = zypp::Pathname(productdatapath + "/cookie");
1301  break;
1302 
1304  default:
1305  break;
1306  }
1307 
1308  // touch the file, ignore error (they are logged anyway)
1310  } catch ( ... ) {
1312  }
1313  return expected<void>::success();
1314  }
1315 
1316  template<typename ZyppContextRefType>
1318  {
1320  }
1321 
1322  template <typename ZyppContextRefType>
1324  {
1325  return touchIndexFile( info, _options );
1326  }
1327 
1328  template <typename ZyppContextRefType>
1330  {
1331  try {
1332  zypp::Pathname dir = _options.knownServicesPath;
1333  std::list<zypp::Pathname> entries;
1334  if (zypp::PathInfo(dir).isExist())
1335  {
1336  if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
1337  {
1338  // TranslatorExplanation '%s' is a pathname
1339  ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
1340  }
1341 
1342  //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
1343  for_(it, entries.begin(), entries.end() )
1344  {
1346  }
1347  }
1348 
1349  zypp::repo::PluginServices(_options.pluginsPath/"services", ServiceCollector(_services));
1350 
1351  return expected<void>::success();
1352 
1353  } catch ( ... ) {
1354  return expected<void>::error( std::current_exception () );
1355  }
1356 
1357  }
1358 
1359  namespace {
1366  inline void cleanupNonRepoMetadtaFolders( const zypp::Pathname & cachePath_r,
1367  const zypp::Pathname & defaultCachePath_r,
1368  const std::list<std::string> & repoEscAliases_r )
1369  {
1371  return;
1372 
1373  if ( cachePath_r != defaultCachePath_r )
1374  return;
1375 
1376  std::list<std::string> entries;
1377  if ( zypp::filesystem::readdir( entries, cachePath_r, false ) == 0 )
1378  {
1379  entries.sort();
1380  std::set<std::string> oldfiles;
1381  set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
1382  std::inserter( oldfiles, oldfiles.end() ) );
1383 
1384  // bsc#1178966: Files or symlinks here have been created by the user
1385  // for whatever purpose. It's our cache, so we purge them now before
1386  // they may later conflict with directories we need.
1387  zypp::PathInfo pi;
1388  for ( const std::string & old : oldfiles )
1389  {
1390  if ( old == zypp::Repository::systemRepoAlias() ) // don't remove the @System solv file
1391  continue;
1392  pi( cachePath_r/old );
1393  if ( pi.isDir() )
1395  else
1397  }
1398  }
1399  }
1400  } // namespace
1401 
1402  template <typename ZyppContextRefType>
1404  {
1405  try {
1406 
1407  MIL << "start construct known repos" << std::endl;
1408 
1409  if ( zypp::PathInfo(_options.knownReposPath).isExist() )
1410  {
1411  std::list<std::string> repoEscAliases;
1412  std::list<RepoInfo> orphanedRepos;
1413  for ( RepoInfo & repoInfo : repositories_in_dir( _zyppContext, _options.knownReposPath ) )
1414  {
1415  // set the metadata path for the repo
1416  repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo).unwrap() );
1417  // set the downloaded packages path for the repo
1418  repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo).unwrap() );
1419  // remember it
1420  _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
1421 
1422  // detect orphaned repos belonging to a deleted service
1423  const std::string & serviceAlias( repoInfo.service() );
1424  if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
1425  {
1426  WAR << "Schedule orphaned service repo for deletion: " << repoInfo << std::endl;
1427  orphanedRepos.push_back( repoInfo );
1428  continue; // don't remember it in repoEscAliases
1429  }
1430 
1431  repoEscAliases.push_back(repoInfo.escaped_alias());
1432  }
1433 
1434  // Cleanup orphanded service repos:
1435  if ( ! orphanedRepos.empty() )
1436  {
1437  for ( const auto & repoInfo : orphanedRepos )
1438  {
1439  MIL << "Delete orphaned service repo " << repoInfo.alias() << std::endl;
1440  // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
1441  // %1% = service name
1442  // %2% = repository name
1443  JobReportHelper(_zyppContext).warning( zypp::str::Format(_("Unknown service '%1%': Removing orphaned service repository '%2%'"))
1444  % repoInfo.service()
1445  % repoInfo.alias() );
1446  try {
1447  removeRepository( repoInfo ).unwrap();
1448  }
1449  catch ( const zypp::Exception & caugth )
1450  {
1452  }
1453  }
1454  }
1455 
1456  // bsc#1210740: Don't cleanup if read-only mode was promised.
1457  if ( not zypp::zypp_readonly_hack::IGotIt() ) {
1458  // delete metadata folders without corresponding repo (e.g. old tmp directories)
1459  //
1460  // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
1461  // we'd need somemagic file to identify zypp cache directories. Without this
1462  // we may easily remove user data (zypper --pkg-cache-dir . download ...)
1463  repoEscAliases.sort();
1464  cleanupNonRepoMetadtaFolders( _options.repoRawCachePath,
1465  zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoMetadataPath() ),
1466  repoEscAliases );
1467  cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath,
1468  zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoSolvfilesPath() ),
1469  repoEscAliases );
1470  // bsc#1204956: Tweak to prevent auto pruning package caches
1471  if ( autoPruneInDir( _options.repoPackagesCachePath ) )
1472  cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath,
1473  zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoPackagesPath() ),
1474  repoEscAliases );
1475  }
1476  }
1477  MIL << "end construct known repos" << std::endl;
1478 
1479  return expected<void>::success();
1480 
1481  } catch ( ... ) {
1482  return expected<void>::error( std::current_exception () );
1483  }
1484  }
1485 
1486  // explicitely intantiate the template types we want to work with
1487  template class RepoManager<SyncContextRef>;
1488  template class RepoManager<ContextRef>;
1489 } // namespace zyppng
std::string asString(const Patch::Category &obj)
Definition: Patch.cc:122
Pathname filepath() const
File where this repo was read from.
static const ValueType day
Definition: Date.h:44
void setBaseUrl(Url url)
Clears current base URL list and adds url.
Definition: RepoInfo.cc:676
void loadFromCache(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Load resolvables into the pool.
Definition: RepoManager.cc:169
Service data.
Definition: ServiceInfo.h:36
std::ostream & dumpAsIniOn(std::ostream &str) const override
Writes ServiceInfo to stream in ".service" format.
Definition: ServiceInfo.cc:176
thrown when it was impossible to match a repository
#define MIL
Definition: Logger.h:100
zypp::RepoStatus RepoStatus
Definition: repomanager.h:37
std::string targetDistro
Definition: repomanager.h:146
bool empty() const
Whether the status is empty (empty checksum)
Definition: RepoStatus.cc:234
Namespace intended to collect all environment variables we use.
Definition: Env.h:24
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:324
#define _(MSG)
Definition: Gettext.h:39
repo::RepoType probe(const Url &url, const Pathname &path) const
Probe repo metadata type.
Definition: RepoManager.cc:175
const Pathname & path() const
Return current Pathname.
Definition: PathInfo.h:251
Read service data from a .service file.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:459
Type toEnum() const
Definition: RepoType.h:49
Regular expression.
Definition: Regex.h:94
std::enable_if_t<!std::is_same_v< void, T >, expected< Container< T >, E > > collect(Container< expected< T, E >, CArgs... > &&in)
Definition: expected.h:501
bool warning(std::string msg_r, UserData userData_r=UserData())
send warning text
MirroredOriginSet repoOrigins() const
The repodata origins.
Definition: RepoInfo.cc:689
expected< T, E > inspect(expected< T, E > exp, Function &&f)
Definition: expected.h:531
AsyncOpRef< expected< zypp::repo::ServiceType > > probeServiceType(ContextRef ctx, const zypp::Url &url)
Definition: serviceswf.cc:306
std::string join(TIterator begin, TIterator end, const C_Str &sep_r=" ")
Join strings using separator sep_r (defaults to BLANK).
Definition: String.h:847
void removeService(const std::string &alias)
Removes service specified by its name.
Definition: RepoManager.cc:249
void cleanCache(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
clean local cache
Definition: RepoManager.cc:163
AsyncOpRef< expected< repo::AsyncRefreshContextRef > > refreshMetadata(repo::AsyncRefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
expected< std::list< RepoInfo > > repositories_in_file(const zypp::Pathname &file)
Reads RepoInfo&#39;s from a repo file.
Definition: repomanager.cc:167
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition: Pathname.h:175
RefreshCheckStatus checkIfToRefreshMetadata(const RepoInfo &info, const Url &url, RawMetadataRefreshPolicy policy=RefreshIfNeeded)
Checks whether to refresh metadata for specified repository and url.
Definition: RepoManager.cc:131
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:27
const char * c_str() const
String representation.
Definition: Pathname.h:112
void setAlias(const std::string &alias)
set the repository alias
Definition: RepoInfoBase.cc:89
std::list< RepoInfo > repositories_in_dir(ZContextRef zyppContext, const zypp::Pathname &dir)
List of RepoInfo&#39;s from a directory.
Definition: repomanager.cc:189
String related utilities and Regular expression matching.
void setFilepath(const Pathname &filename)
set the path to the .repo file
Definition: RepoInfoBase.cc:95
bool isTmpRepo(const RepoInfo &info_r)
Whether repo is not under RM control and provides its own methadata paths.
Definition: repomanager.h:55
Definition: Arch.h:363
What is known about a repository.
Definition: RepoInfo.h:71
static expected< std::decay_t< Type >, Err > make_expected_success(Type &&t)
Definition: expected.h:397
AsyncOpRef< expected< repo::AsyncRefreshContextRef > > buildCache(repo::AsyncRefreshContextRef refCtx, zypp::RepoManagerFlags::CacheBuildPolicy policy, ProgressObserverRef progressObserver)
Service already exists and some unique attribute can&#39;t be duplicated.
Convenient building of std::string with boost::format.
Definition: String.h:253
void buildCache(const RepoInfo &info, CacheBuildPolicy policy=BuildIfNeeded, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Refresh local cache.
Definition: RepoManager.cc:156
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:39
bool enabled() const
If enabled is false, then this repository must be ignored as if does not exists, except when checking...
Definition: RepoInfoBase.cc:98
repo::ServiceType probeService(const Url &url) const
Probe the type or the service.
Definition: RepoManager.cc:240
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition: Exception.h:463
ZyppContextRefType _zyppContext
#define zypp_defer
Definition: AutoDispose.h:293
Url::asString() view options.
Definition: UrlBase.h:40
zypp::RepoManager::RefreshServiceOptions _options
Definition: serviceswf.cc:748
int recursive_rmdir(const Pathname &path)
Like &#39;rm -r DIR&#39;.
Definition: PathInfo.cc:417
endpoint_iterator end()
~RepoManager()
Dtor.
Definition: RepoManager.cc:84
expected< zypp::Pathname > packagescache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the packages cache path for a repository.
Definition: repomanager.h:195
Extract credentials in Url authority and store them via CredentialManager.
static expected< repo::RefreshContextRef< ZyppContextRefType > > create(ZyppContextRefType zyppContext, zypp::RepoInfo info, RepoManagerRef< ContextRefType > repoManager)
Definition: refresh.cc:32
Repo manager settings.
void cleanCacheDirGarbage(const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Remove any subdirectories of cache directories which no longer belong to any of known repositories...
Definition: RepoManager.cc:172
void refreshService(const std::string &alias, const RefreshServiceOptions &options_r=RefreshServiceOptions())
Refresh specific service.
Definition: RepoManager.cc:258
void refreshMetadata(const RepoInfo &info, RawMetadataRefreshPolicy policy=RefreshIfNeeded, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Refresh local raw cache.
Definition: RepoManager.cc:140
ResultType or_else(const expected< T, E > &exp, Function &&f)
Definition: expected.h:463
Simple callback to collect the results.
Definition: repomanager.h:134
Container< Ret > transform(Container< Msg, CArgs... > &&val, Transformation &&transformation)
Definition: transform.h:31
Url mirrorListUrl() const
Url of a file which contains a list of repository urls.
Definition: RepoInfo.cc:745
void modifyRepository(const std::string &alias, const RepoInfo &newinfo, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Modify repository attributes.
Definition: RepoManager.cc:205
Exp mtry(F &&f, Args &&...args)
Definition: mtry.h:28
bool empty() const
Test for an empty path.
Definition: Pathname.h:116
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:515
expected< zypp::Pathname > rawproductdata_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the raw product metadata path for a repository, this is inside the raw cache dir...
Definition: repomanager.h:186
static RepoStatus fromCookieFileUseMtime(const Pathname &path)
Reads the status from a cookie file but uses the files mtime.
Definition: RepoStatus.cc:217
void refreshServices(const RefreshServiceOptions &options_r=RefreshServiceOptions())
Refreshes all enabled services.
Definition: RepoManager.cc:255
void cleanPackages(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Clean local package cache.
Definition: RepoManager.cc:150
RepoInfo getRepositoryInfo(const std::string &alias, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Find a matching repository info.
Definition: RepoManager.cc:216
bool collect(const RepoInfo &repo)
Definition: repomanager.cc:148
void removeRepository(const RepoInfo &repo)
Log recently removed repository.
Definition: HistoryLog.cc:301
int touch(const Pathname &path)
Change file&#39;s modification and access times.
Definition: PathInfo.cc:1242
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition: String.h:212
Lightweight repository attribute value lookup.
Definition: LookupAttr.h:264
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
thrown when it was impossible to determine one url for this repo.
Definition: RepoException.h:78
Manages a data source characterized by an authoritative URL and a list of mirror URLs.
const std::string & asString() const
String representation.
Definition: Pathname.h:93
std::string alias() const
unique identifier for this source.
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:286
void addService(const std::string &alias, const Url &url)
Adds a new service by its alias and URL.
Definition: RepoManager.cc:243
auto joinPipeline(ContextRef ctx, AsyncOpRef< T > res)
Definition: context.h:81
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition: Exception.cc:140
static const SolvAttr repositoryToolVersion
Definition: SolvAttr.h:193
expected< void > assert_alias(const RepoInfo &info)
Definition: repomanager.h:58
creates and provides information about known sources.
Definition: RepoManager.cc:38
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:126
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:272
#define WAR
Definition: Logger.h:101
void setMetadataPath(const Pathname &path)
Set the path where the local metadata is stored.
Definition: RepoInfo.cc:709
void setType(const repo::RepoType &t)
set the repository type
Definition: RepoInfo.cc:702
bool ZYPP_PLUGIN_APPDATA_FORCE_COLLECT()
To trigger appdata refresh unconditionally.
Definition: repomanager.cc:55
bool repoOriginsEmpty() const
whether repo origins are available
Definition: RepoInfo.cc:694
void addRepository(const RepoInfo &repo)
Log a newly added repository.
Definition: HistoryLog.cc:289
Writing the zypp history fileReference counted signleton for writhing the zypp history file...
Definition: HistoryLog.h:56
RepoInfoList repos
Definition: repomanager.h:145
Read repository data from a .repo file.
void modifyService(const std::string &oldAlias, const ServiceInfo &service)
Modifies service file (rewrites it with new values) and underlying repositories if needed...
Definition: RepoManager.cc:264
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition: PathInfo.cc:610
Base Exception for service handling.
bool isValid() const
Verifies the Url.
Definition: Url.cc:507
std::list< Url > url_set
Definition: RepoInfo.h:108
bool requireStatusWithMediaFile() const
Returns true if this repository requires the media.1/media file to be included in the metadata status...
Definition: RepoInfo.cc:1123
RepoSet::const_iterator RepoConstIterator
Definition: repomanager.h:299
std::vector< std::string > Arguments
const std::string & asString() const
Return current Pathname as String.
Definition: PathInfo.h:253
std::string numstring(char n, int w=0)
Definition: String.h:290
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:705
static const RepoType NONE
Definition: RepoType.h:33
static expected success(ConsParams &&...params)
Definition: expected.h:115
Url url() const
The service url.
Definition: ServiceInfo.cc:102
zypp::RepoManagerFlags::RefreshServiceOptions RefreshServiceOptions
Definition: repomanager.h:265
void setPackagesPath(const Pathname &path)
set the path where the local packages are stored
Definition: RepoInfo.cc:712
std::ostream & copy(std::istream &from_r, std::ostream &to_r)
Copy istream to ostream.
Definition: IOStream.h:51
bool userMayRX() const
Definition: PathInfo.h:358
static const RepoType RPMMD
Definition: RepoType.h:30
bool autoPruneInDir(const zypp::Pathname &path_r)
bsc#1204956: Tweak to prevent auto pruning package caches.
Definition: repomanager.cc:234
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:475
#define ZYPP_PRIVATE_CONSTR_ARG
Definition: zyppglobal.h:160
AsyncOpRef< expected< void > > refreshGeoIPData(ContextRef ctx, RepoInfo::url_set urls)
static const RepoType YAST2
Definition: RepoType.h:31
bool error(std::string msg_r, UserData userData_r=UserData())
send error text
AsyncOpRef< expected< zypp::repo::RepoType > > probeRepoType(ContextRef ctx, AsyncLazyMediaHandle medium, zypp::Pathname path, std::optional< zypp::Pathname > targetPath)
expected< void > assert_urls(const RepoInfo &info)
Definition: repomanager.cc:227
void addRepository(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Adds a repository to the list of known repositories.
Definition: RepoManager.cc:181
const RepoStates & repoStates() const
Access the remembered repository states.
Definition: ServiceInfo.cc:164
Base class for Exception.
Definition: Exception.h:152
std::ostream & dumpAsIniOn(std::ostream &str) const override
Write this RepoInfo object into str in a .repo file format.
Definition: RepoInfo.cc:995
Exception for repository handling.
Definition: RepoException.h:37
std::set< ServiceInfo > ServiceSet
ServiceInfo typedefs.
Definition: repomanager.h:293
refresh is delayed due to settings
bool any_of(const Container &c, Fnc &&cb)
Definition: Algorithm.h:76
static Date now()
Return the current time.
Definition: Date.h:78
AsyncOpRef< expected< void > > refreshService(AsyncRepoManagerRef repoMgr, ServiceInfo info, zypp::RepoManagerFlags::RefreshServiceOptions options)
Definition: serviceswf.cc:763
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition: String.h:500
Iterator findAlias(const std::string &alias_r, Iterator begin_r, Iterator end_r)
Find alias_r in repo/service container.
Definition: repomanager.h:99
The repository cache is not built yet so you can&#39;t create the repostories from the cache...
Definition: RepoException.h:65
void eraseFromPool()
Remove this Repository from its Pool.
Definition: Repository.cc:298
std::string targetDistribution() const
Distribution for which is this repository meant.
Definition: RepoInfo.cc:781
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:225
bool regex_match(const std::string &s, smatch &matches, const regex &regex)
regex ZYPP_STR_REGEX regex ZYPP_STR_REGEX
Definition: Regex.h:70
void removeRepository(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Remove the best matching repository from known repos list.
Definition: RepoManager.cc:198
AsyncOpRef< expected< repo::RefreshCheckStatus > > checkIfToRefreshMetadata(repo::AsyncRefreshContextRef refCtx, LazyMediaHandle< Provide > medium, ProgressObserverRef progressObserver)
RefreshCheckStatus
Possibly return state of RepoManager::checkIfToRefreshMetadata function.
ResultType and_then(const expected< T, E > &exp, Function &&f)
Definition: expected.h:423
static const RepoType RPMPLAINDIR
Definition: RepoType.h:32
static const std::string & systemRepoAlias()
Reserved system repository alias .
Definition: Repository.cc:38
expected< zypp::Pathname > rawcache_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the raw cache path for a repository, this is usually /var/cache/zypp/alias.
Definition: repomanager.h:172
RepoStatus metadataStatus(const RepoInfo &info) const
Status of local metadata.
Definition: RepoManager.cc:125
Track changing files or directories.
Definition: RepoStatus.h:40
The RepoManager class Provides knowledge and methods to maintain repo settings and metadata for a giv...
Definition: repomanager.h:247
AsyncOpRef< expected< RepoInfo > > addRepository(AsyncRepoManagerRef mgr, RepoInfo info, ProgressObserverRef myProgress)
Repository already exists and some unique attribute can&#39;t be duplicated.
std::string filenameFromAlias(const std::string &alias_r, const std::string &stem_r)
Generate a related filename from a repo/service infos alias.
Definition: repomanager.cc:137
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:333
void modifyRepository(const RepoInfo &oldrepo, const RepoInfo &newrepo)
Log certain modifications to a repository.
Definition: HistoryLog.cc:312
#define ZYPP_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
Definition: Exception.h:471
std::ostream & operator<<(std::ostream &str, zypp::RepoManagerFlags::RawMetadataRefreshPolicy obj)
Definition: repomanager.cc:101
AsyncOpRef< expected< void > > addRepositories(AsyncRepoManagerRef mgr, zypp::Url url, ProgressObserverRef myProgress)
Represents a single, configurable network endpoint, combining a URL with specific access settings...
void refreshGeoIp(const RepoInfo::url_set &urls)
Definition: RepoManager.cc:267
repo::ServiceType type() const
Service type.
Definition: ServiceInfo.cc:111
endpoint_iterator begin()
std::string label() const
Label for use in messages for the user interface.
url_set baseUrls() const
The complete set of repository urls as configured.
Definition: RepoInfo.cc:769
repo::RepoType type() const
Type of repository,.
Definition: RepoInfo.cc:742
bool collect(const Url &url_r)
Remember credentials stored in URL authority leaving the password in url_r.
auto incProgress(ProgressObserverRef progressObserver, double progrIncrease=1.0, std::optional< std::string > newStr={})
void cleanMetadata(const RepoInfo &info, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Clean local metadata.
Definition: RepoManager.cc:147
Functor collecting ServiceInfos into a ServiceSet.
Definition: repomanager.h:215
#define OUTS(V)
Url manipulation class.
Definition: Url.h:92
expected< zypp::Pathname > solv_path_for_repoinfo(const RepoManagerOptions &opt, const RepoInfo &info)
Calculates the solv cache path for a repository.
Definition: repomanager.h:205
Repository type enumeration.
Definition: RepoType.h:28
void addRepositories(const Url &url, const ProgressData::ReceiverFnc &progressrcv=ProgressData::ReceiverFnc())
Adds repositores from a repo file to the list of known repositories.
Definition: RepoManager.cc:195