libzypp  17.37.5
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.effectiveBaseUrlsEmpty() )
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 Urls
599  zypp::UrlCredentialExtractor( _options.rootDir ).collect( tosave.effectiveBaseUrls() );
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
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 &repourl : it->effectiveBaseUrls() )
813  {
814  if ( repourl.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, urls, policy]( zyppng::repo::RefreshContextRef<ZyppContextRefType> &&refCtx ) {
835  refCtx->setPolicy ( static_cast<zyppng::repo::RawMetadataRefreshPolicy>( policy ) );
836  return _zyppContext->provider()->prepareMedia( urls, zyppng::ProvideMediaSpec() )
837  | and_then( [ r = std::move(refCtx) ]( auto mediaHandle ) mutable { return zyppng::RepoManagerWorkflow::checkIfToRefreshMetadata ( std::move(r), std::move(mediaHandle), nullptr ); } );
838  })
839  );
840  }
841 
842  template<typename ZyppContextRefType>
844  {
845  using namespace zyppng::operators;
846  // helper callback in case the repo type changes on the remote
847  // do NOT capture by reference here, since this is possibly executed async
848  const auto &updateProbedType = [this, info = info]( zypp::repo::RepoType repokind ) {
849  // update probed type only for repos in system
850  for( const auto &repo : repos() ) {
851  if ( info.alias() == repo.alias() )
852  {
853  RepoInfo modifiedrepo = repo;
854  modifiedrepo.setType( repokind );
855  // don't modify .repo in refresh.
856  // modifyRepository( info.alias(), modifiedrepo );
857  break;
858  }
859  }
860  };
861 
862  return joinPipeline( _zyppContext,
863  // make sure geoIP data is up 2 date, but ignore errors
865  | [this, info = info](auto) { return zyppng::repo::RefreshContext<ZyppContextRefType>::create( _zyppContext, info, shared_this<RepoManager<ZyppContextRefType>>()); }
866  | and_then( [policy, myProgress, cb = updateProbedType]( repo::RefreshContextRef<ZyppContextRefType> refCtx ) {
867  refCtx->setPolicy( static_cast<repo::RawMetadataRefreshPolicy>( policy ) );
868  // in case probe detects a different repokind, update our internal repos
870 
871  return zyppng::RepoManagerWorkflow::refreshMetadata ( std::move(refCtx), myProgress );
872  })
873  | and_then([rMgr = shared_this<RepoManager<ZyppContextRefType>>()]( repo::RefreshContextRef<ZyppContextRefType> ctx ) {
874 
875  if ( ! isTmpRepo( ctx->repoInfo() ) )
876  rMgr->reposManip(); // remember to trigger appdata refresh
877 
878  return expected<void>::success ();
879  }));
880  }
881 
882  template<typename ZyppContextRefType>
883  std::vector<std::pair<RepoInfo, expected<void>>> RepoManager<ZyppContextRefType>::refreshMetadata( std::vector<RepoInfo> infos, RawMetadataRefreshPolicy policy, ProgressObserverRef myProgress )
884  {
885  using namespace zyppng::operators;
886 
887  ProgressObserver::setup( myProgress, "Refreshing repositories" , 1 );
888 
889  auto r = std::move(infos)
890  | transform( [this, policy, myProgress]( const RepoInfo &info ) {
891 
892  auto subProgress = ProgressObserver::makeSubTask( myProgress, 1.0, zypp::str::Str() << _("Refreshing Repository: ") << info.alias(), 3 );
893 
894  // helper callback in case the repo type changes on the remote
895  // do NOT capture by reference here, since this is possibly executed async
896  const auto &updateProbedType = [this, info = info]( zypp::repo::RepoType repokind ) {
897  // update probed type only for repos in system
898  for( const auto &repo : repos() ) {
899  if ( info.alias() == repo.alias() )
900  {
901  RepoInfo modifiedrepo = repo;
902  modifiedrepo.setType( repokind );
903  // don't modify .repo in refresh.
904  // modifyRepository( info.alias(), modifiedrepo );
905  break;
906  }
907  }
908  };
909 
910  auto sharedThis = shared_this<RepoManager<ZyppContextRefType>>();
911 
912  return
913  // make sure geoIP data is up 2 date, but ignore errors
915  | [sharedThis, info = info](auto) { return zyppng::repo::RefreshContext<ZyppContextRefType>::create( sharedThis->_zyppContext, info, sharedThis); }
916  | inspect( incProgress( subProgress ) )
917  | and_then( [policy, subProgress, cb = updateProbedType]( repo::RefreshContextRef<ZyppContextRefType> refCtx ) {
918  refCtx->setPolicy( static_cast<repo::RawMetadataRefreshPolicy>( policy ) );
919  // in case probe detects a different repokind, update our internal repos
921 
922  return zyppng::RepoManagerWorkflow::refreshMetadata ( std::move(refCtx), ProgressObserver::makeSubTask( subProgress ) );
923  })
924  | inspect( incProgress( subProgress ) )
925  | and_then([subProgress]( repo::RefreshContextRef<ZyppContextRefType> ctx ) {
926 
927  if ( ! isTmpRepo( ctx->repoInfo() ) )
928  ctx->repoManager()->reposManip(); // remember to trigger appdata refresh
929 
930  return zyppng::RepoManagerWorkflow::buildCache ( std::move(ctx), CacheBuildPolicy::BuildIfNeeded, ProgressObserver::makeSubTask( subProgress ) );
931  })
932  | inspect( incProgress( subProgress ) )
933  | [ info = info, subProgress ]( expected<repo::RefreshContextRef<ZyppContextRefType>> result ) {
934  if ( result ) {
935  ProgressObserver::finish( subProgress, ProgressObserver::Success );
936  return std::make_pair(info, expected<void>::success() );
937  } else {
938  ProgressObserver::finish( subProgress, ProgressObserver::Error );
939  return std::make_pair(info, expected<void>::error( result.error() ) );
940  }
941  };
942  }
943  | [myProgress]( auto res ) {
944  ProgressObserver::finish( myProgress, ProgressObserver::Success );
945  return res;
946  }
947  );
948 
949  return joinPipeline( _zyppContext, r );
950  }
951 
958  template<typename ZyppContextRefType>
959  expected<zypp::repo::RepoType> RepoManager<ZyppContextRefType>::probe(const std::vector<zypp::Url> &urls, const zypp::Pathname &path) const
960  {
961  using namespace zyppng::operators;
962 
963  return joinPipeline( _zyppContext,
965  | [this, urls=urls](auto) { return _zyppContext->provider()->prepareMedia( urls, zyppng::ProvideMediaSpec() ); }
966  | and_then( [this, path = path]( auto mediaHandle ) {
967  return RepoManagerWorkflow::probeRepoType( _zyppContext, std::forward<decltype(mediaHandle)>(mediaHandle), path );
968  }));
969  }
970 
971  template<typename ZyppContextRefType>
972  expected<void> RepoManager<ZyppContextRefType>::buildCache( const RepoInfo &info, CacheBuildPolicy policy, ProgressObserverRef myProgress )
973  {
974  using namespace zyppng::operators;
975  return joinPipeline( _zyppContext,
977  | and_then( [policy, myProgress]( repo::RefreshContextRef<ZyppContextRefType> refCtx ) {
978  return zyppng::RepoManagerWorkflow::buildCache ( std::move(refCtx), policy, myProgress );
979  })
980  | and_then([]( auto ){ return expected<void>::success(); })
981  );
982  }
983 
984  template<typename ZyppContextRefType>
986  {
987  return joinPipeline( _zyppContext, RepoManagerWorkflow::addRepository( shared_this<RepoManager<ZyppContextRefType>>(), info, std::move(myProgress) ) );
988  }
989 
990  template<typename ZyppContextRefType>
992  {
993  using namespace zyppng::operators;
994  return joinPipeline( _zyppContext, RepoManagerWorkflow::addRepositories( shared_this<RepoManager<ZyppContextRefType>>(), url, std::move(myProgress)));
995  }
996 
997  template <typename ZyppContextRefType>
999  {
1001  }
1002 
1003  template <typename ZyppContextRefType>
1005  {
1006  try {
1007 
1008  assert_alias( service ).unwrap();
1009 
1010  // check if service already exists
1011  if ( hasService( service.alias() ) )
1013 
1014  // Writable ServiceInfo is needed to save the location
1015  // of the .service file. Finaly insert into the service list.
1016  ServiceInfo toSave( service );
1017  saveService( toSave ).unwrap();
1018  _services.insert( toSave );
1019 
1020  // check for credentials in Url
1021  zypp::UrlCredentialExtractor( _options.rootDir ).collect( toSave.url() );
1022 
1023  MIL << "added service " << toSave.alias() << std::endl;
1024 
1025  } catch ( ... ) {
1026  return expected<void>::error( std::current_exception () );
1027  }
1028 
1029  return expected<void>::success();
1030  }
1031 
1032  template<typename ZyppContextRefType>
1034  {
1035  return joinPipeline ( _zyppContext, RepoServicesWorkflow::refreshService( shared_this<RepoManager<ZyppContextRefType>>(), getService( alias ), options_r ) );
1036  }
1037 
1041  template<typename ZyppContextRefType>
1043  {
1044  using namespace zyppng::operators;
1045  // copy the set of services since refreshService
1046  // can eventually invalidate the iterator
1047  ServiceSet servicesCopy( serviceBegin(), serviceEnd() );
1048 
1049  // convert the set into a vector, transform needs a container with push_back support
1050  std::vector<ServiceInfo> servicesVec;
1051  std::copy( std::make_move_iterator(servicesCopy.begin()), std::make_move_iterator(servicesCopy.end()), std::back_inserter(servicesVec));
1052 
1053  return joinPipeline( _zyppContext,
1054  std::move(servicesVec)
1055  | transform( [options_r, this]( ServiceInfo i ){ return RepoServicesWorkflow::refreshService( shared_this<RepoManager<ZyppContextRefType>>(), i, options_r ); } )
1056  | join()
1057  | collect()
1058  );
1059  }
1060 
1062 
1063  template <typename ZyppContextRefType>
1065  {
1066  try {
1067  MIL << "Going to delete service " << alias << std::endl;
1068 
1069  const ServiceInfo & service = getService( alias );
1070 
1071  zypp::Pathname location = service.filepath();
1072  if( location.empty() )
1073  {
1074  ZYPP_THROW(zypp::repo::ServiceException( service, _("Can't figure out where the service is stored.") ));
1075  }
1076 
1077  ServiceSet tmpSet;
1079 
1080  // only one service definition in the file
1081  if ( tmpSet.size() == 1 )
1082  {
1083  if ( zypp::filesystem::unlink(location) != 0 )
1084  {
1085  // TranslatorExplanation '%s' is a filename
1086  ZYPP_THROW(zypp::repo::ServiceException( service, zypp::str::form( _("Can't delete '%s'"), location.c_str() ) ));
1087  }
1088  MIL << alias << " successfully deleted." << std::endl;
1089  }
1090  else
1091  {
1093 
1094  std::ofstream file(location.c_str());
1095  if( !file )
1096  {
1097  // TranslatorExplanation '%s' is a filename
1098  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), location.c_str() )));
1099  }
1100 
1101  for_(it, tmpSet.begin(), tmpSet.end())
1102  {
1103  if( it->alias() != alias )
1104  it->dumpAsIniOn(file);
1105  }
1106 
1107  MIL << alias << " successfully deleted from file " << location << std::endl;
1108  }
1109 
1110  // now remove all repositories added by this service
1111  RepoCollector rcollector;
1112  getRepositoriesInService( alias,
1113  boost::make_function_output_iterator( std::bind( &RepoCollector::collect, &rcollector, std::placeholders::_1 ) ) );
1114  // cannot do this directly in getRepositoriesInService - would invalidate iterators
1115  for_(rit, rcollector.repos.begin(), rcollector.repos.end())
1116  removeRepository(*rit).unwrap();
1117 
1118  return expected<void>::success();
1119 
1120  } catch ( ... ) {
1121  return expected<void>::error( std::current_exception () );
1122  }
1123  }
1124 
1125  template <typename ZyppContextRefType>
1126  expected<void> RepoManager<ZyppContextRefType>::modifyService( const std::string & oldAlias, const ServiceInfo & newService )
1127  {
1128  try {
1129 
1130  MIL << "Going to modify service " << oldAlias << std::endl;
1131 
1132  // we need a writable copy to link it to the file where
1133  // it is saved if we modify it
1134  ServiceInfo service(newService);
1135 
1136  if ( service.type() == zypp::repo::ServiceType::PLUGIN )
1137  {
1139  }
1140 
1141  const ServiceInfo & oldService = getService(oldAlias);
1142 
1143  zypp::Pathname location = oldService.filepath();
1144  if( location.empty() )
1145  {
1146  ZYPP_THROW(zypp::repo::ServiceException( oldService, _("Can't figure out where the service is stored.") ));
1147  }
1148 
1149  // remember: there may multiple services being defined in one file:
1150  ServiceSet tmpSet;
1152 
1154  std::ofstream file(location.c_str());
1155  for_(it, tmpSet.begin(), tmpSet.end())
1156  {
1157  if( *it != oldAlias )
1158  it->dumpAsIniOn(file);
1159  }
1160  service.dumpAsIniOn(file);
1161  file.close();
1162  service.setFilepath(location);
1163 
1164  _services.erase(oldAlias);
1165  _services.insert(service);
1166  // check for credentials in Urls
1167  zypp::UrlCredentialExtractor( _options.rootDir ).collect( service.url() );
1168 
1169 
1170  // changed properties affecting also repositories
1171  if ( oldAlias != service.alias() // changed alias
1172  || oldService.enabled() != service.enabled() ) // changed enabled status
1173  {
1174  std::vector<RepoInfo> toModify;
1175  getRepositoriesInService(oldAlias, std::back_inserter(toModify));
1176  for_( it, toModify.begin(), toModify.end() )
1177  {
1178  if ( oldService.enabled() != service.enabled() )
1179  {
1180  if ( service.enabled() )
1181  {
1182  // reset to last refreshs state
1183  const auto & last = service.repoStates().find( it->alias() );
1184  if ( last != service.repoStates().end() )
1185  it->setEnabled( last->second.enabled );
1186  }
1187  else
1188  it->setEnabled( false );
1189  }
1190 
1191  if ( oldAlias != service.alias() )
1192  it->setService(service.alias());
1193 
1194  modifyRepository(it->alias(), *it).unwrap();
1195  }
1196  }
1197 
1198  return expected<void>::success();
1199 
1200  } catch ( ... ) {
1201  return expected<void>::error( std::current_exception () );
1202  }
1203 
1205  }
1206 
1207 
1208  template <typename ZyppContextRefType>
1210  {
1211  try {
1212 
1213  zypp::filesystem::assert_dir( _options.knownServicesPath );
1214  zypp::Pathname servfile = generateNonExistingName( _options.knownServicesPath,
1215  generateFilename( service ) );
1216  service.setFilepath( servfile );
1217 
1218  MIL << "saving service in " << servfile << std::endl;
1219 
1220  std::ofstream file( servfile.c_str() );
1221  if ( !file )
1222  {
1223  // TranslatorExplanation '%s' is a filename
1224  ZYPP_THROW( zypp::Exception(zypp::str::form( _("Can't open file '%s' for writing."), servfile.c_str() )));
1225  }
1226  service.dumpAsIniOn( file );
1227  MIL << "done" << std::endl;
1228 
1229  return expected<void>::success();
1230 
1231  } catch ( ... ) {
1232  return expected<void>::error( std::current_exception () );
1233  }
1234  }
1235 
1251  template <typename ZyppContextRefType>
1253  const std::string & basefilename ) const
1254  {
1255  std::string final_filename = basefilename;
1256  int counter = 1;
1257  while ( zypp::PathInfo(dir + final_filename).isExist() )
1258  {
1259  final_filename = basefilename + "_" + zypp::str::numstring(counter);
1260  ++counter;
1261  }
1262  return dir + zypp::Pathname(final_filename);
1263  }
1264 
1265  template <typename ZyppContextRefType>
1267  {
1268  try {
1269  zypp::Pathname productdatapath = rawproductdata_path_for_repoinfo( options, info ).unwrap();
1270 
1271  zypp::repo::RepoType repokind = info.type();
1272  if ( repokind.toEnum() == zypp::repo::RepoType::NONE_e )
1273  // unknown, probe the local metadata
1274  repokind = probeCache( productdatapath );
1275  // if still unknown, just return
1276  if (repokind == zypp::repo::RepoType::NONE_e)
1277  return expected<void>::success();
1278 
1279  zypp::Pathname p;
1280  switch ( repokind.toEnum() )
1281  {
1283  p = zypp::Pathname(productdatapath + "/repodata/repomd.xml");
1284  break;
1285 
1287  p = zypp::Pathname(productdatapath + "/content");
1288  break;
1289 
1291  p = zypp::Pathname(productdatapath + "/cookie");
1292  break;
1293 
1295  default:
1296  break;
1297  }
1298 
1299  // touch the file, ignore error (they are logged anyway)
1301  } catch ( ... ) {
1303  }
1304  return expected<void>::success();
1305  }
1306 
1307  template<typename ZyppContextRefType>
1309  {
1311  }
1312 
1313  template <typename ZyppContextRefType>
1315  {
1316  return touchIndexFile( info, _options );
1317  }
1318 
1319  template <typename ZyppContextRefType>
1321  {
1322  try {
1323  zypp::Pathname dir = _options.knownServicesPath;
1324  std::list<zypp::Pathname> entries;
1325  if (zypp::PathInfo(dir).isExist())
1326  {
1327  if ( zypp::filesystem::readdir( entries, dir, false ) != 0 )
1328  {
1329  // TranslatorExplanation '%s' is a pathname
1330  ZYPP_THROW(zypp::Exception(zypp::str::form(_("Failed to read directory '%s'"), dir.c_str())));
1331  }
1332 
1333  //str::regex allowedServiceExt("^\\.service(_[0-9]+)?$");
1334  for_(it, entries.begin(), entries.end() )
1335  {
1337  }
1338  }
1339 
1340  zypp::repo::PluginServices(_options.pluginsPath/"services", ServiceCollector(_services));
1341 
1342  return expected<void>::success();
1343 
1344  } catch ( ... ) {
1345  return expected<void>::error( std::current_exception () );
1346  }
1347 
1348  }
1349 
1350  namespace {
1357  inline void cleanupNonRepoMetadtaFolders( const zypp::Pathname & cachePath_r,
1358  const zypp::Pathname & defaultCachePath_r,
1359  const std::list<std::string> & repoEscAliases_r )
1360  {
1362  return;
1363 
1364  if ( cachePath_r != defaultCachePath_r )
1365  return;
1366 
1367  std::list<std::string> entries;
1368  if ( zypp::filesystem::readdir( entries, cachePath_r, false ) == 0 )
1369  {
1370  entries.sort();
1371  std::set<std::string> oldfiles;
1372  set_difference( entries.begin(), entries.end(), repoEscAliases_r.begin(), repoEscAliases_r.end(),
1373  std::inserter( oldfiles, oldfiles.end() ) );
1374 
1375  // bsc#1178966: Files or symlinks here have been created by the user
1376  // for whatever purpose. It's our cache, so we purge them now before
1377  // they may later conflict with directories we need.
1378  zypp::PathInfo pi;
1379  for ( const std::string & old : oldfiles )
1380  {
1381  if ( old == zypp::Repository::systemRepoAlias() ) // don't remove the @System solv file
1382  continue;
1383  pi( cachePath_r/old );
1384  if ( pi.isDir() )
1386  else
1388  }
1389  }
1390  }
1391  } // namespace
1392 
1393  template <typename ZyppContextRefType>
1395  {
1396  try {
1397 
1398  MIL << "start construct known repos" << std::endl;
1399 
1400  if ( zypp::PathInfo(_options.knownReposPath).isExist() )
1401  {
1402  std::list<std::string> repoEscAliases;
1403  std::list<RepoInfo> orphanedRepos;
1404  for ( RepoInfo & repoInfo : repositories_in_dir( _zyppContext, _options.knownReposPath ) )
1405  {
1406  // set the metadata path for the repo
1407  repoInfo.setMetadataPath( rawcache_path_for_repoinfo(_options, repoInfo).unwrap() );
1408  // set the downloaded packages path for the repo
1409  repoInfo.setPackagesPath( packagescache_path_for_repoinfo(_options, repoInfo).unwrap() );
1410  // remember it
1411  _reposX.insert( repoInfo ); // direct access via _reposX in ctor! no reposManip.
1412 
1413  // detect orphaned repos belonging to a deleted service
1414  const std::string & serviceAlias( repoInfo.service() );
1415  if ( ! ( serviceAlias.empty() || hasService( serviceAlias ) ) )
1416  {
1417  WAR << "Schedule orphaned service repo for deletion: " << repoInfo << std::endl;
1418  orphanedRepos.push_back( repoInfo );
1419  continue; // don't remember it in repoEscAliases
1420  }
1421 
1422  repoEscAliases.push_back(repoInfo.escaped_alias());
1423  }
1424 
1425  // Cleanup orphanded service repos:
1426  if ( ! orphanedRepos.empty() )
1427  {
1428  for ( const auto & repoInfo : orphanedRepos )
1429  {
1430  MIL << "Delete orphaned service repo " << repoInfo.alias() << std::endl;
1431  // translators: Cleanup a repository previously owned by a meanwhile unknown (deleted) service.
1432  // %1% = service name
1433  // %2% = repository name
1434  JobReportHelper(_zyppContext).warning( zypp::str::Format(_("Unknown service '%1%': Removing orphaned service repository '%2%'"))
1435  % repoInfo.service()
1436  % repoInfo.alias() );
1437  try {
1438  removeRepository( repoInfo ).unwrap();
1439  }
1440  catch ( const zypp::Exception & caugth )
1441  {
1443  }
1444  }
1445  }
1446 
1447  // bsc#1210740: Don't cleanup if read-only mode was promised.
1448  if ( not zypp::zypp_readonly_hack::IGotIt() ) {
1449  // delete metadata folders without corresponding repo (e.g. old tmp directories)
1450  //
1451  // bnc#891515: Auto-cleanup only zypp.conf default locations. Otherwise
1452  // we'd need somemagic file to identify zypp cache directories. Without this
1453  // we may easily remove user data (zypper --pkg-cache-dir . download ...)
1454  repoEscAliases.sort();
1455  cleanupNonRepoMetadtaFolders( _options.repoRawCachePath,
1456  zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoMetadataPath() ),
1457  repoEscAliases );
1458  cleanupNonRepoMetadtaFolders( _options.repoSolvCachePath,
1459  zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoSolvfilesPath() ),
1460  repoEscAliases );
1461  // bsc#1204956: Tweak to prevent auto pruning package caches
1462  if ( autoPruneInDir( _options.repoPackagesCachePath ) )
1463  cleanupNonRepoMetadtaFolders( _options.repoPackagesCachePath,
1464  zypp::Pathname::assertprefix( _options.rootDir, _zyppContext->config().builtinRepoPackagesPath() ),
1465  repoEscAliases );
1466  }
1467  }
1468  MIL << "end construct known repos" << std::endl;
1469 
1470  return expected<void>::success();
1471 
1472  } catch ( ... ) {
1473  return expected<void>::error( std::current_exception () );
1474  }
1475  }
1476 
1477  // explicitely intantiate the template types we want to work with
1478  template class RepoManager<SyncContextRef>;
1479  template class RepoManager<ContextRef>;
1480 } // 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:693
bool effectiveBaseUrlsEmpty() const
whether effective repository urls are available
Definition: RepoInfo.cc:706
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
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
~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
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
url_set effectiveBaseUrls() const
The complete set of effective repository urls.
Definition: RepoInfo.cc:711
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
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:726
void setType(const repo::RepoType &t)
set the repository type
Definition: RepoInfo.cc:719
bool ZYPP_PLUGIN_APPDATA_FORCE_COLLECT()
To trigger appdata refresh unconditionally.
Definition: repomanager.cc:55
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.
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:1143
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:729
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:1015
Exception for repository handling.
Definition: RepoException.h:37
std::set< ServiceInfo > ServiceSet
ServiceInfo typedefs.
Definition: repomanager.h:293
refresh is delayed due to settings
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:798
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)
void refreshGeoIp(const RepoInfo::url_set &urls)
Definition: RepoManager.cc:267
repo::ServiceType type() const
Service type.
Definition: ServiceInfo.cc:111
std::string label() const
Label for use in messages for the user interface.
repo::RepoType type() const
Type of repository,.
Definition: RepoInfo.cc:759
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